From bf7cef945176c801232d022fef8425d8a1dae142 Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Tue, 7 Jul 2026 14:26:44 -0500 Subject: [PATCH 01/60] Alerts API implementation --- README.md | 4 ++-- pom.xml | 2 +- src/main/java/com/cta4j/alert/AlertApi.java | 9 +++++++++ .../com/cta4j/alert/routestatus/RouteStatusApi.java | 7 +++++++ .../cta4j/alert/routestatus/model/ServiceType.java | 11 +++++++++++ 5 files changed, 30 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/cta4j/alert/AlertApi.java create mode 100644 src/main/java/com/cta4j/alert/routestatus/RouteStatusApi.java create mode 100644 src/main/java/com/cta4j/alert/routestatus/model/ServiceType.java diff --git a/README.md b/README.md index f1938ffe..ddb0bcc5 100644 --- a/README.md +++ b/README.md @@ -36,13 +36,13 @@ After applying, you'll receive an API key by email. Keep it safe — you'll use com.cta4j cta4j-java-sdk - 6.0.0 + 6.1.0 ``` ### Gradle (Kotlin DSL) ```kotlin -implementation("com.cta4j:cta4j-java-sdk:6.0.0") +implementation("com.cta4j:cta4j-java-sdk:6.1.0") ``` --- diff --git a/pom.xml b/pom.xml index aaffd475..dcbe7718 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ 4.0.0 com.cta4j cta4j-java-sdk - 6.0.0 + 6.1.0 21 UTF-8 diff --git a/src/main/java/com/cta4j/alert/AlertApi.java b/src/main/java/com/cta4j/alert/AlertApi.java new file mode 100644 index 00000000..ce1b97b7 --- /dev/null +++ b/src/main/java/com/cta4j/alert/AlertApi.java @@ -0,0 +1,9 @@ +package com.cta4j.alert; + +import com.cta4j.alert.routestatus.RouteStatusApi; +import org.jspecify.annotations.NullMarked; + +@NullMarked +public interface AlertApi { + RouteStatusApi routeStatus(); +} diff --git a/src/main/java/com/cta4j/alert/routestatus/RouteStatusApi.java b/src/main/java/com/cta4j/alert/routestatus/RouteStatusApi.java new file mode 100644 index 00000000..f37bdb8f --- /dev/null +++ b/src/main/java/com/cta4j/alert/routestatus/RouteStatusApi.java @@ -0,0 +1,7 @@ +package com.cta4j.alert.routestatus; + +import org.jspecify.annotations.NullMarked; + +@NullMarked +public interface RouteStatusApi { +} diff --git a/src/main/java/com/cta4j/alert/routestatus/model/ServiceType.java b/src/main/java/com/cta4j/alert/routestatus/model/ServiceType.java new file mode 100644 index 00000000..1e6acb3a --- /dev/null +++ b/src/main/java/com/cta4j/alert/routestatus/model/ServiceType.java @@ -0,0 +1,11 @@ +package com.cta4j.alert.routestatus.model; + +import org.jspecify.annotations.NullMarked; + +@NullMarked +public enum ServiceType { + BUS, + RAIL, + STATION, + SYSTEMWIDE +} From 687a05aac4811f08a9f3c5601cfc6a056e346c90 Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Sat, 11 Jul 2026 14:34:02 -0500 Subject: [PATCH 02/60] Route status API --- .../common/exception/Cta4jAlertException.java | 67 +++++++++++ .../common/internal/mapper/Qualifiers.java | 30 +++++ .../internal/util/AlertApiConstants.java | 20 ++++ .../alert/routestatus/RouteStatusApi.java | 87 ++++++++++++++ .../exception/Cta4jRouteStatusException.java | 51 +++++++++ .../exception/RouteStatusErrorCode.java | 108 ++++++++++++++++++ .../internal/mapper/RouteStatusMapper.java | 22 ++++ .../internal/wire/CtaRouteInfo.java | 31 +++++ .../internal/wire/CtaRouteInfoUrl.java | 19 +++ .../internal/wire/CtaRouteStatusResponse.java | 19 +++ .../routestatus/internal/wire/CtaRoutes.java | 50 ++++++++ .../alert/routestatus/model/RouteStatus.java | 54 +++++++++ 12 files changed, 558 insertions(+) create mode 100644 src/main/java/com/cta4j/alert/common/exception/Cta4jAlertException.java create mode 100644 src/main/java/com/cta4j/alert/common/internal/mapper/Qualifiers.java create mode 100644 src/main/java/com/cta4j/alert/common/internal/util/AlertApiConstants.java create mode 100644 src/main/java/com/cta4j/alert/routestatus/exception/Cta4jRouteStatusException.java create mode 100644 src/main/java/com/cta4j/alert/routestatus/exception/RouteStatusErrorCode.java create mode 100644 src/main/java/com/cta4j/alert/routestatus/internal/mapper/RouteStatusMapper.java create mode 100644 src/main/java/com/cta4j/alert/routestatus/internal/wire/CtaRouteInfo.java create mode 100644 src/main/java/com/cta4j/alert/routestatus/internal/wire/CtaRouteInfoUrl.java create mode 100644 src/main/java/com/cta4j/alert/routestatus/internal/wire/CtaRouteStatusResponse.java create mode 100644 src/main/java/com/cta4j/alert/routestatus/internal/wire/CtaRoutes.java create mode 100644 src/main/java/com/cta4j/alert/routestatus/model/RouteStatus.java diff --git a/src/main/java/com/cta4j/alert/common/exception/Cta4jAlertException.java b/src/main/java/com/cta4j/alert/common/exception/Cta4jAlertException.java new file mode 100644 index 00000000..f2b92e11 --- /dev/null +++ b/src/main/java/com/cta4j/alert/common/exception/Cta4jAlertException.java @@ -0,0 +1,67 @@ +package com.cta4j.alert.common.exception; + +import com.cta4j.common.exception.Cta4jException; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +/** + * A custom exception class for handling cta4j alert-specific errors. + */ +@NullMarked +public class Cta4jAlertException extends Cta4jException { + /** + * The raw error code associated with this exception, if available. + */ + @Nullable + private final Integer rawErrorCode; + + /** + * Constructs a {@code Cta4jAlertException}. + * + * @param message the detail message + * @param endpoint the endpoint associated with the exception + * @throws NullPointerException if {@code endpoint} is {@code null} + */ + public Cta4jAlertException(String message, String endpoint) { + super(message, endpoint); + + this.rawErrorCode = null; + } + + /** + * Constructs a {@code Cta4jAlertException}. + * + * @param message the detail message + * @param endpoint the endpoint associated with the exception + * @param cause the cause of the exception + * @throws NullPointerException if {@code endpoint} is {@code null} + */ + public Cta4jAlertException(String message, String endpoint, Throwable cause) { + super(message, endpoint, cause); + + this.rawErrorCode = null; + } + + /** + * Constructs a {@code Cta4jAlertException} with a raw error code. + * + * @param message the detail message + * @param endpoint the endpoint associated with the exception + * @param rawErrorCode the raw error code associated with the exception + * @throws NullPointerException if {@code endpoint} is {@code null} + */ + public Cta4jAlertException(String message, String endpoint, int rawErrorCode) { + super(message, endpoint); + + this.rawErrorCode = rawErrorCode; + } + + /** + * Returns the raw error code associated with this exception, if available. + * + * @return the raw error code, or {@code null} if not available + */ + public @Nullable Integer getRawErrorCode() { + return this.rawErrorCode; + } +} diff --git a/src/main/java/com/cta4j/alert/common/internal/mapper/Qualifiers.java b/src/main/java/com/cta4j/alert/common/internal/mapper/Qualifiers.java new file mode 100644 index 00000000..eda760b4 --- /dev/null +++ b/src/main/java/com/cta4j/alert/common/internal/mapper/Qualifiers.java @@ -0,0 +1,30 @@ +package com.cta4j.alert.common.internal.mapper; + +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; +import org.mapstruct.Named; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Objects; + +@ApiStatus.Internal +@NullMarked +public final class Qualifiers { + private Qualifiers() { + throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); + } + + @Named("mapUri") + public static URI mapUri(String value) { + Objects.requireNonNull(value); + + try { + return new URI(value); + } catch (URISyntaxException e) { + String message = "Failed to parse URI: %s".formatted(value); + + throw new IllegalArgumentException(message, e); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/cta4j/alert/common/internal/util/AlertApiConstants.java b/src/main/java/com/cta4j/alert/common/internal/util/AlertApiConstants.java new file mode 100644 index 00000000..71a928be --- /dev/null +++ b/src/main/java/com/cta4j/alert/common/internal/util/AlertApiConstants.java @@ -0,0 +1,20 @@ +package com.cta4j.alert.common.internal.util; + +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; + +@ApiStatus.Internal +@NullMarked +public final class AlertApiConstants { + public static final String SCHEME = "https"; + public static final String DEFAULT_HOST = "www.transitchicago.com"; + + private static final String API_PREFIX = "/api/1.0"; + + public static final String DETAILED_ALERTS_ENDPOINT = "%s/alerts.aspx".formatted(API_PREFIX); + public static final String ROUTE_STATUS_ENDPOINT = "%s/routes.aspx".formatted(API_PREFIX); + + private AlertApiConstants() { + throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); + } +} diff --git a/src/main/java/com/cta4j/alert/routestatus/RouteStatusApi.java b/src/main/java/com/cta4j/alert/routestatus/RouteStatusApi.java index f37bdb8f..f6be8b14 100644 --- a/src/main/java/com/cta4j/alert/routestatus/RouteStatusApi.java +++ b/src/main/java/com/cta4j/alert/routestatus/RouteStatusApi.java @@ -1,7 +1,94 @@ package com.cta4j.alert.routestatus; +import com.cta4j.alert.routestatus.exception.Cta4jRouteStatusException; +import com.cta4j.alert.routestatus.model.RouteStatus; +import com.cta4j.alert.routestatus.model.ServiceType; import org.jspecify.annotations.NullMarked; +import java.util.Collection; +import java.util.List; +import java.util.Objects; + +/** + * Provides access to route status-related endpoints of the CTA Alerts API. + *

+ * This API allows retrieval of the status of all bus and train routes, or filtered by service type, route ID, or + * station ID. + */ @NullMarked public interface RouteStatusApi { + /** + * Retrieves the status of all bus and train routes. + * + * @return a {@link List} of {@link RouteStatus}es, or an empty {@link List} if no route statuses are found + * @throws Cta4jRouteStatusException if the API returns an error response or the response cannot be parsed + */ + List list(); + + /** + * Retrieves route statuses by their service types. + * + * @param types a {@link Collection} of service types + * @return a {@link List} of {@link RouteStatus}es corresponding to the provided types, or an empty {@link List} + * if no route statuses are found + * @throws NullPointerException if {@code types} is {@code null} or contains {@code null} elements + * @throws Cta4jRouteStatusException if the API returns an error response or the response cannot be parsed + */ + List findByTypes(Collection types); + + /** + * Retrieves route statuses by a service type. + * + * @param type the service type + * @return a {@link List} of {@link RouteStatus}es corresponding to the provided type, or an empty {@link List} + * if no route statuses are found + * @throws NullPointerException if {@code type} is {@code null} + * @throws Cta4jRouteStatusException if the API returns an error response or the response cannot be parsed + */ + default List findByType(ServiceType type) { + Objects.requireNonNull(type); + + List types = List.of(type); + + return this.findByTypes(types); + } + + /** + * Retrieves route statuses for the specified route IDs. + * + * @param routeIds a {@link Collection} of route IDs + * @return a {@link List} of {@link RouteStatus}es associated with the route IDs, or an empty {@link List} if no + * route statuses are found for the route IDs + * @throws NullPointerException if {@code routeIds} is {@code null} or contains {@code null} elements + * @throws Cta4jRouteStatusException if the API returns an error response or the response cannot be parsed + */ + List findByRouteIds(Collection routeIds); + + /** + * Retrieves route statuses for the specified route ID. + * + * @param routeId the route ID + * @return a {@link List} of {@link RouteStatus}es associated with the route ID, or an empty {@link List} if no + * route statuses are found for the route ID + * @throws NullPointerException if {@code routeId} is {@code null} + * @throws Cta4jRouteStatusException if the API returns an error response or the response cannot be parsed + */ + default List findByRouteId(String routeId) { + Objects.requireNonNull(routeId); + + List routeIds = List.of(routeId); + + return this.findByRouteIds(routeIds); + } + + /** + * Retrieves route statuses for the specified station ID. + * + * @param stationId the station ID + * @return a {@link List} of {@link RouteStatus}es associated with the station ID, or an empty {@link List} if + * no route statuses are found for the station ID + * @throws NullPointerException if {@code stationId} is {@code null} + * @throws Cta4jRouteStatusException if the API returns an error response or the response cannot be parsed + */ + List findByStationId(String stationId); } diff --git a/src/main/java/com/cta4j/alert/routestatus/exception/Cta4jRouteStatusException.java b/src/main/java/com/cta4j/alert/routestatus/exception/Cta4jRouteStatusException.java new file mode 100644 index 00000000..d82c5659 --- /dev/null +++ b/src/main/java/com/cta4j/alert/routestatus/exception/Cta4jRouteStatusException.java @@ -0,0 +1,51 @@ +package com.cta4j.alert.routestatus.exception; + +import com.cta4j.alert.common.exception.Cta4jAlertException; +import com.cta4j.alert.common.internal.util.AlertApiConstants; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +/** + * A custom exception class for handling cta4j alert route status-specific errors. + */ +@NullMarked +public final class Cta4jRouteStatusException extends Cta4jAlertException { + /** + * The error code associated with this exception, if available. + */ + @Nullable + private final RouteStatusErrorCode errorCode; + + /** + * Constructs a {@code Cta4jRouteStatusException}. + * + * @param message the detail message + * @param cause the cause of the exception + */ + public Cta4jRouteStatusException(String message, Throwable cause) { + super(message, AlertApiConstants.ROUTE_STATUS_ENDPOINT, cause); + + this.errorCode = null; + } + + /** + * Constructs a {@code Cta4jRouteStatusException}. + * + * @param message the detail message + * @param rawErrorCode the raw error code associated with the exception + */ + public Cta4jRouteStatusException(String message, int rawErrorCode) { + super(message, AlertApiConstants.ROUTE_STATUS_ENDPOINT, rawErrorCode); + + this.errorCode = RouteStatusErrorCode.fromCode(rawErrorCode); + } + + /** + * Returns the error code associated with this exception, if available. + * + * @return the error code, or {@code null} if not available + */ + public @Nullable RouteStatusErrorCode getErrorCode() { + return this.errorCode; + } +} diff --git a/src/main/java/com/cta4j/alert/routestatus/exception/RouteStatusErrorCode.java b/src/main/java/com/cta4j/alert/routestatus/exception/RouteStatusErrorCode.java new file mode 100644 index 00000000..080fba75 --- /dev/null +++ b/src/main/java/com/cta4j/alert/routestatus/exception/RouteStatusErrorCode.java @@ -0,0 +1,108 @@ +package com.cta4j.alert.routestatus.exception; + +import org.jspecify.annotations.NullMarked; + +/** + * Represents the error codes returned by the CTA Route Status API. + */ +@NullMarked +public enum RouteStatusErrorCode { + /** + * Indicates that the request was successful and there were no errors. + */ + OK(0), + + /** + * Indicates that no routes or stations matched the provided filter criteria. + *

+ * This code is not documented in the CTA Alerts API documentation for the Route Status API, but has been + * observed in practice. + */ + NO_RESULTS(50), + + /** + * Indicates that the provided station ID is not an integer. + */ + STATIONID_NOT_INTEGER(100), + + /** + * Indicates that the provided service type is invalid. + */ + INVALID_TYPE(101), + + /** + * Indicates that the "routeid" and "stationid" parameters were both provided, which is not allowed. + */ + ROUTEID_STATIONID_CONFLICT(102), + + /** + * Indicates that the "routeid" and "type" parameters were both provided, which is not allowed. + */ + ROUTEID_TYPE_CONFLICT(103), + + /** + * Indicates that the "stationid" and "type" parameters were both provided, which is not allowed. + */ + STATIONID_TYPE_CONFLICT(104), + + /** + * Indicates that the query string contains a parameter that is not recognized by the API. The supported API + * parameters are "type", "routeid", "stationid", and "outputType". + */ + INVALID_PARAMETER(500), + + /** + * Indicates that the server encountered an unexpected error that prevented it from fulfilling the request. + */ + SERVER_ERROR(900), + + /** + * Indicates that an unknown error occurred that does not match any of the defined error codes. + */ + UNKNOWN(-1); + + /** + * The integer code associated with this error code. + */ + private final int code; + + /** + * Constructs a {@code RouteStatusErrorCode}. + * + * @param code the integer code associated with the error code + */ + RouteStatusErrorCode(int code) { + this.code = code; + } + + /** + * Returns the integer code associated with this error code. + * + * @return the integer code + */ + public int getCode() { + return this.code; + } + + /** + * Returns the {@code RouteStatusErrorCode} corresponding to the given integer code. + * + * @param code the integer code to look up + * @return the corresponding {@code RouteStatusErrorCode}, or {@code UNKNOWN} if the code does not match any + * defined error code + */ + public static RouteStatusErrorCode fromCode(int code) { + return switch (code) { + case 0 -> OK; + case 50 -> NO_RESULTS; + case 100 -> STATIONID_NOT_INTEGER; + case 101 -> INVALID_TYPE; + case 102 -> ROUTEID_STATIONID_CONFLICT; + case 103 -> ROUTEID_TYPE_CONFLICT; + case 104 -> STATIONID_TYPE_CONFLICT; + case 500 -> INVALID_PARAMETER; + case 900 -> SERVER_ERROR; + default -> UNKNOWN; + }; + } +} \ No newline at end of file diff --git a/src/main/java/com/cta4j/alert/routestatus/internal/mapper/RouteStatusMapper.java b/src/main/java/com/cta4j/alert/routestatus/internal/mapper/RouteStatusMapper.java new file mode 100644 index 00000000..06fde909 --- /dev/null +++ b/src/main/java/com/cta4j/alert/routestatus/internal/mapper/RouteStatusMapper.java @@ -0,0 +1,22 @@ +package com.cta4j.alert.routestatus.internal.mapper; + +import com.cta4j.alert.common.internal.mapper.Qualifiers; +import com.cta4j.alert.routestatus.internal.wire.CtaRouteInfo; +import com.cta4j.alert.routestatus.model.RouteStatus; +import org.jetbrains.annotations.ApiStatus; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +@Mapper(uses = Qualifiers.class) +@ApiStatus.Internal +public interface RouteStatusMapper { + RouteStatusMapper INSTANCE = Mappers.getMapper(RouteStatusMapper.class); + + @Mapping(target = "color", source = "routeColorCode") + @Mapping(target = "textColor", source = "routeTextColor") + @Mapping(target = "url", source = "routeUrl.cdataSection", qualifiedByName = "mapUri") + @Mapping(target = "status", source = "routeStatus") + @Mapping(target = "statusColor", source = "routeStatusColor") + RouteStatus toDomain(CtaRouteInfo routeInfo); +} diff --git a/src/main/java/com/cta4j/alert/routestatus/internal/wire/CtaRouteInfo.java b/src/main/java/com/cta4j/alert/routestatus/internal/wire/CtaRouteInfo.java new file mode 100644 index 00000000..a9f63512 --- /dev/null +++ b/src/main/java/com/cta4j/alert/routestatus/internal/wire/CtaRouteInfo.java @@ -0,0 +1,31 @@ +package com.cta4j.alert.routestatus.internal.wire; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; + +import java.util.Objects; + +@JsonIgnoreProperties(ignoreUnknown = true) +@ApiStatus.Internal +@NullMarked +public record CtaRouteInfo( + @JsonProperty("Route") String route, + @JsonProperty("RouteColorCode") String routeColorCode, + @JsonProperty("RouteTextColor") String routeTextColor, + @JsonProperty("ServiceId") String serviceId, + @JsonProperty("RouteURL") CtaRouteInfoUrl routeUrl, + @JsonProperty("RouteStatus") String routeStatus, + @JsonProperty("RouteStatusColor") String routeStatusColor +) { + public CtaRouteInfo { + Objects.requireNonNull(route); + Objects.requireNonNull(routeColorCode); + Objects.requireNonNull(routeTextColor); + Objects.requireNonNull(serviceId); + Objects.requireNonNull(routeUrl); + Objects.requireNonNull(routeStatus); + Objects.requireNonNull(routeStatusColor); + } +} diff --git a/src/main/java/com/cta4j/alert/routestatus/internal/wire/CtaRouteInfoUrl.java b/src/main/java/com/cta4j/alert/routestatus/internal/wire/CtaRouteInfoUrl.java new file mode 100644 index 00000000..4d9f39d2 --- /dev/null +++ b/src/main/java/com/cta4j/alert/routestatus/internal/wire/CtaRouteInfoUrl.java @@ -0,0 +1,19 @@ +package com.cta4j.alert.routestatus.internal.wire; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; + +import java.util.Objects; + +@JsonIgnoreProperties(ignoreUnknown = true) +@ApiStatus.Internal +@NullMarked +public record CtaRouteInfoUrl( + @JsonProperty("#cdata-section") String cdataSection +) { + public CtaRouteInfoUrl { + Objects.requireNonNull(cdataSection); + } +} diff --git a/src/main/java/com/cta4j/alert/routestatus/internal/wire/CtaRouteStatusResponse.java b/src/main/java/com/cta4j/alert/routestatus/internal/wire/CtaRouteStatusResponse.java new file mode 100644 index 00000000..d298a55d --- /dev/null +++ b/src/main/java/com/cta4j/alert/routestatus/internal/wire/CtaRouteStatusResponse.java @@ -0,0 +1,19 @@ +package com.cta4j.alert.routestatus.internal.wire; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; + +import java.util.Objects; + +@JsonIgnoreProperties(ignoreUnknown = true) +@ApiStatus.Internal +@NullMarked +public record CtaRouteStatusResponse( + @JsonProperty("CTARoutes") CtaRoutes ctaRoutes +) { + public CtaRouteStatusResponse { + Objects.requireNonNull(ctaRoutes); + } +} diff --git a/src/main/java/com/cta4j/alert/routestatus/internal/wire/CtaRoutes.java b/src/main/java/com/cta4j/alert/routestatus/internal/wire/CtaRoutes.java new file mode 100644 index 00000000..ccc12260 --- /dev/null +++ b/src/main/java/com/cta4j/alert/routestatus/internal/wire/CtaRoutes.java @@ -0,0 +1,50 @@ +package com.cta4j.alert.routestatus.internal.wire; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +@JsonIgnoreProperties(ignoreUnknown = true) +@ApiStatus.Internal +@NullMarked +public record CtaRoutes( + @JsonProperty("TimeStamp") + String timestamp, + + @JsonProperty("ErrorCode") + @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY) + List errorCode, + + @JsonProperty("ErrorMessage") + @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY) + @Nullable + List<@Nullable String> errorMessage, + + @JsonProperty("RouteInfo") + @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY) + @Nullable + List routeInfo +) { + public CtaRoutes { + Objects.requireNonNull(timestamp); + Objects.requireNonNull(errorCode); + + errorCode = List.copyOf(errorCode); + + if (errorMessage != null) { + errorMessage = Collections.unmodifiableList(new ArrayList<>(errorMessage)); + } + + if (routeInfo != null) { + routeInfo = List.copyOf(routeInfo); + } + } +} diff --git a/src/main/java/com/cta4j/alert/routestatus/model/RouteStatus.java b/src/main/java/com/cta4j/alert/routestatus/model/RouteStatus.java new file mode 100644 index 00000000..43a9f41e --- /dev/null +++ b/src/main/java/com/cta4j/alert/routestatus/model/RouteStatus.java @@ -0,0 +1,54 @@ +package com.cta4j.alert.routestatus.model; + +import org.jspecify.annotations.NullMarked; + +import java.net.URI; +import java.util.Objects; + +/** + * Represents the service status of a single route. + * + * @param route the name of this route (e.g., "Red Line") + * @param color the color of this route used in maps, as {@code rrggbb} (e.g., "c60c30") + * @param textColor the suggested color of text displayed against {@code color}, as {@code rrggbb} (e.g., "ffffff") + * @param serviceId the unique GTFS route or station identifier of this route (e.g., "Red") + * @param url the URL of this route's or station's page on transitchicago.com + * @param status the ultimate, human-readable status of this route (e.g., "Normal service", "Planned work", "Minor + * delays") + * @param statusColor the suggested color associated with {@code status}, as {@code rrggbb} (e.g., "404040") + */ +@NullMarked +public record RouteStatus( + String route, + String color, + String textColor, + String serviceId, + URI url, + String status, + String statusColor +) { + /** + * Constructs a {@code RouteStatus}. + * + * @param route the name of the route (e.g., "Red Line") + * @param color the color of the route used in maps, as {@code rrggbb} (e.g., "c60c30") + * @param textColor the suggested color of text displayed against {@code color}, as {@code rrggbb} (e.g., + * "ffffff") + * @param serviceId the unique GTFS route or station identifier of the route (e.g., "Red") + * @param url the URL of the route's or station's page on transitchicago.com + * @param status the ultimate, human-readable status of the route (e.g., "Normal service", "Planned work", + * "Minor delays") + * @param statusColor the suggested color associated with {@code status}, as {@code rrggbb} (e.g., "404040") + * @throws NullPointerException if {@code route}, {@code color}, {@code textColor}, {@code serviceId}, + * {@code url}, {@code status}, or {@code statusColor} is {@code null} + */ + public RouteStatus { + Objects.requireNonNull(route); + Objects.requireNonNull(color); + Objects.requireNonNull(textColor); + Objects.requireNonNull(serviceId); + Objects.requireNonNull(url); + Objects.requireNonNull(status); + Objects.requireNonNull(statusColor); + } +} \ No newline at end of file From 2443c705a0e678ff539f16ef0982eecd83e1a7e6 Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Mon, 13 Jul 2026 16:14:10 -0500 Subject: [PATCH 03/60] Route status API --- .../internal/config/AlertApiConfig.java | 25 ++++ .../internal/impl/RouteStatusApiImpl.java | 118 ++++++++++++++++++ .../routestatus/internal/wire/CtaRoutes.java | 14 +-- 3 files changed, 145 insertions(+), 12 deletions(-) create mode 100644 src/main/java/com/cta4j/alert/common/internal/config/AlertApiConfig.java create mode 100644 src/main/java/com/cta4j/alert/routestatus/internal/impl/RouteStatusApiImpl.java diff --git a/src/main/java/com/cta4j/alert/common/internal/config/AlertApiConfig.java b/src/main/java/com/cta4j/alert/common/internal/config/AlertApiConfig.java new file mode 100644 index 00000000..a7f10ce2 --- /dev/null +++ b/src/main/java/com/cta4j/alert/common/internal/config/AlertApiConfig.java @@ -0,0 +1,25 @@ +package com.cta4j.alert.common.internal.config; + +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; + +import java.util.Objects; + +@ApiStatus.Internal +@NullMarked +public record AlertApiConfig( + String scheme, + String host, + int port, + String apiKey +) { + public AlertApiConfig { + Objects.requireNonNull(scheme); + Objects.requireNonNull(host); + Objects.requireNonNull(apiKey); + } + + public AlertApiConfig(String scheme, String host, String apiKey) { + this(scheme, host, -1, apiKey); + } +} diff --git a/src/main/java/com/cta4j/alert/routestatus/internal/impl/RouteStatusApiImpl.java b/src/main/java/com/cta4j/alert/routestatus/internal/impl/RouteStatusApiImpl.java new file mode 100644 index 00000000..9982801b --- /dev/null +++ b/src/main/java/com/cta4j/alert/routestatus/internal/impl/RouteStatusApiImpl.java @@ -0,0 +1,118 @@ +package com.cta4j.alert.routestatus.internal.impl; + +import com.cta4j.alert.common.internal.config.AlertApiConfig; +import com.cta4j.alert.routestatus.RouteStatusApi; +import com.cta4j.alert.routestatus.exception.Cta4jRouteStatusException; +import com.cta4j.alert.routestatus.exception.RouteStatusErrorCode; +import com.cta4j.alert.routestatus.internal.mapper.RouteStatusMapper; +import com.cta4j.alert.routestatus.internal.wire.CtaRouteInfo; +import com.cta4j.alert.routestatus.internal.wire.CtaRouteStatusResponse; +import com.cta4j.alert.routestatus.internal.wire.CtaRoutes; +import com.cta4j.alert.routestatus.model.RouteStatus; +import com.cta4j.alert.routestatus.model.ServiceType; +import org.apache.hc.client5.http.fluent.Request; +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; +import tools.jackson.core.JacksonException; +import tools.jackson.databind.json.JsonMapper; + +import java.io.IOException; +import java.util.Collection; +import java.util.List; +import java.util.Objects; + +@ApiStatus.Internal +@NullMarked +public final class RouteStatusApiImpl implements RouteStatusApi { + private final AlertApiConfig config; + + public RouteStatusApiImpl(AlertApiConfig config) { + this.config = config; + } + + @Override + public List list() { + return List.of(); + } + + @Override + public List findByTypes(Collection types) { + Objects.requireNonNull(types); + + List typesList = List.copyOf(types); + + return List.of(); + } + + @Override + public List findByRouteIds(Collection routeIds) { + Objects.requireNonNull(routeIds); + + List routeIdsList = List.copyOf(routeIds); + + return List.of(); + } + + @Override + public List findByStationId(String stationId) { + Objects.requireNonNull(stationId); + + return List.of(); + } + + private List makeRequest(String url) { + String response; + + try { + response = Request.get(url) + .execute() + .returnContent() + .asString(); + } catch (IOException e) { + String message = Objects.requireNonNullElse(e.getMessage(), "Request failed"); + + throw new Cta4jRouteStatusException(message, e); + } + + CtaRouteStatusResponse routeStatusResponse; + + try { + routeStatusResponse = JsonMapper.shared() + .readValue(response, CtaRouteStatusResponse.class); + } catch (JacksonException e) { + throw new Cta4jRouteStatusException("Failed to parse response", e); + } + + CtaRoutes ctaRoutes = routeStatusResponse.ctaRoutes(); + + List routeInfo = ctaRoutes.routeInfo(); + + if (routeInfo != null && !routeInfo.isEmpty()) { + return routeInfo.stream() + .map(RouteStatusMapper.INSTANCE::toDomain) + .toList(); + } + + int integerCode; + + try { + integerCode = Integer.parseInt(ctaRoutes.errorCode()); + } catch (NumberFormatException e) { + throw new Cta4jRouteStatusException("Failed to parse error code", e); + } + + RouteStatusErrorCode errorCode = RouteStatusErrorCode.fromCode(integerCode); + + if (errorCode == RouteStatusErrorCode.OK || errorCode == RouteStatusErrorCode.NO_RESULTS) { + return List.of(); + } + + String errorMessage = ctaRoutes.errorMessage(); + + String message = errorMessage == null || errorMessage.isBlank() + ? "An unknown error occurred." + : errorMessage; + + throw new Cta4jRouteStatusException(message, integerCode); + } +} diff --git a/src/main/java/com/cta4j/alert/routestatus/internal/wire/CtaRoutes.java b/src/main/java/com/cta4j/alert/routestatus/internal/wire/CtaRoutes.java index ccc12260..21b8a141 100644 --- a/src/main/java/com/cta4j/alert/routestatus/internal/wire/CtaRoutes.java +++ b/src/main/java/com/cta4j/alert/routestatus/internal/wire/CtaRoutes.java @@ -7,8 +7,6 @@ import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Objects; @@ -20,13 +18,11 @@ public record CtaRoutes( String timestamp, @JsonProperty("ErrorCode") - @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY) - List errorCode, + String errorCode, @JsonProperty("ErrorMessage") - @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY) @Nullable - List<@Nullable String> errorMessage, + String errorMessage, @JsonProperty("RouteInfo") @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY) @@ -37,12 +33,6 @@ public record CtaRoutes( Objects.requireNonNull(timestamp); Objects.requireNonNull(errorCode); - errorCode = List.copyOf(errorCode); - - if (errorMessage != null) { - errorMessage = Collections.unmodifiableList(new ArrayList<>(errorMessage)); - } - if (routeInfo != null) { routeInfo = List.copyOf(routeInfo); } From 570c79c499efca873b4a6badba6dc41d9b236d66 Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Mon, 13 Jul 2026 16:35:54 -0500 Subject: [PATCH 04/60] Route status API --- .../internal/impl/RouteStatusApiImpl.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/cta4j/alert/routestatus/internal/impl/RouteStatusApiImpl.java b/src/main/java/com/cta4j/alert/routestatus/internal/impl/RouteStatusApiImpl.java index 9982801b..b0b9b6f1 100644 --- a/src/main/java/com/cta4j/alert/routestatus/internal/impl/RouteStatusApiImpl.java +++ b/src/main/java/com/cta4j/alert/routestatus/internal/impl/RouteStatusApiImpl.java @@ -1,6 +1,7 @@ package com.cta4j.alert.routestatus.internal.impl; import com.cta4j.alert.common.internal.config.AlertApiConfig; +import com.cta4j.alert.common.internal.util.AlertApiConstants; import com.cta4j.alert.routestatus.RouteStatusApi; import com.cta4j.alert.routestatus.exception.Cta4jRouteStatusException; import com.cta4j.alert.routestatus.exception.RouteStatusErrorCode; @@ -11,6 +12,7 @@ import com.cta4j.alert.routestatus.model.RouteStatus; import com.cta4j.alert.routestatus.model.ServiceType; import org.apache.hc.client5.http.fluent.Request; +import org.apache.hc.core5.net.URIBuilder; import org.jetbrains.annotations.ApiStatus; import org.jspecify.annotations.NullMarked; import tools.jackson.core.JacksonException; @@ -32,7 +34,15 @@ public RouteStatusApiImpl(AlertApiConfig config) { @Override public List list() { - return List.of(); + String url = new URIBuilder() + .setScheme(this.config.scheme()) + .setHost(this.config.host()) + .setPort(this.config.port()) + .setPath(AlertApiConstants.ROUTE_STATUS_ENDPOINT) + .addParameter("outputType", "JSON") + .toString(); + + return this.makeRequest(url); } @Override From 7c52ae1f6be60926c326c584aa668289c025bf9d Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Thu, 16 Jul 2026 16:34:19 -0500 Subject: [PATCH 05/60] Route status API --- README.md | 4 +- pom.xml | 2 +- .../alert/routestatus/RouteStatusApi.java | 56 ++++++++++++++----- .../internal/impl/RouteStatusApiImpl.java | 51 ++++++++++++++++- .../alert/routestatus/model/RouteStatus.java | 29 +++++----- .../routestatus/model/TrainRouteStatus.java | 52 +++++++++++++++++ .../model => common/train}/TrainLine.java | 2 +- .../internal/impl/ArrivalsApiImpl.java | 2 +- .../train/arrival/query/MapArrivalQuery.java | 2 +- .../train/arrival/query/StopArrivalQuery.java | 2 +- .../common/internal/mapper/Qualifiers.java | 2 +- .../com/cta4j/train/common/model/Arrival.java | 1 + .../cta4j/train/location/LocationsApi.java | 2 +- .../internal/impl/LocationsApiImpl.java | 2 +- .../train/location/model/TrainLocations.java | 2 +- .../cta4j/train/station/model/Station.java | 2 +- .../train/arrival/ArrivalMapperTest.java | 2 +- .../train/arrival/ArrivalsApiImplTest.java | 2 +- .../arrival/query/MapArrivalQueryTest.java | 2 +- .../arrival/query/StopArrivalQueryTest.java | 2 +- .../train/common/TrainQualifiersTest.java | 2 +- .../train/common/model/TrainLineTest.java | 1 + .../train/location/LocationsApiImplTest.java | 2 +- .../location/TrainLocationsMapperTest.java | 2 +- .../train/station/StationMapperTest.java | 2 +- .../train/station/StationsApiImplTest.java | 2 +- 26 files changed, 181 insertions(+), 51 deletions(-) create mode 100644 src/main/java/com/cta4j/alert/routestatus/model/TrainRouteStatus.java rename src/main/java/com/cta4j/{train/common/model => common/train}/TrainLine.java (98%) diff --git a/README.md b/README.md index ddb0bcc5..ba1fcc80 100644 --- a/README.md +++ b/README.md @@ -36,13 +36,13 @@ After applying, you'll receive an API key by email. Keep it safe — you'll use com.cta4j cta4j-java-sdk - 6.1.0 + 7.0.0 ``` ### Gradle (Kotlin DSL) ```kotlin -implementation("com.cta4j:cta4j-java-sdk:6.1.0") +implementation("com.cta4j:cta4j-java-sdk:7.0.0") ``` --- diff --git a/pom.xml b/pom.xml index dcbe7718..fbbfd223 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ 4.0.0 com.cta4j cta4j-java-sdk - 6.1.0 + 7.0.0 21 UTF-8 diff --git a/src/main/java/com/cta4j/alert/routestatus/RouteStatusApi.java b/src/main/java/com/cta4j/alert/routestatus/RouteStatusApi.java index f6be8b14..e4bafbef 100644 --- a/src/main/java/com/cta4j/alert/routestatus/RouteStatusApi.java +++ b/src/main/java/com/cta4j/alert/routestatus/RouteStatusApi.java @@ -3,6 +3,8 @@ import com.cta4j.alert.routestatus.exception.Cta4jRouteStatusException; import com.cta4j.alert.routestatus.model.RouteStatus; import com.cta4j.alert.routestatus.model.ServiceType; +import com.cta4j.alert.routestatus.model.TrainRouteStatus; +import com.cta4j.common.train.TrainLine; import org.jspecify.annotations.NullMarked; import java.util.Collection; @@ -12,8 +14,8 @@ /** * Provides access to route status-related endpoints of the CTA Alerts API. *

- * This API allows retrieval of the status of all bus and train routes, or filtered by service type, route ID, or - * station ID. + * This API allows retrieval of the status of all bus and train routes, or filtered by service type, bus route ID, + * train line, or station ID. */ @NullMarked public interface RouteStatusApi { @@ -54,31 +56,59 @@ default List findByType(ServiceType type) { } /** - * Retrieves route statuses for the specified route IDs. + * Retrieves route statuses for the specified bus route IDs. * - * @param routeIds a {@link Collection} of route IDs - * @return a {@link List} of {@link RouteStatus}es associated with the route IDs, or an empty {@link List} if no - * route statuses are found for the route IDs + * @param routeIds a {@link Collection} of bus route IDs + * @return a {@link List} of {@link RouteStatus}es associated with the bus route IDs, or an empty {@link List} if + * no route statuses are found for the bus route IDs * @throws NullPointerException if {@code routeIds} is {@code null} or contains {@code null} elements * @throws Cta4jRouteStatusException if the API returns an error response or the response cannot be parsed */ - List findByRouteIds(Collection routeIds); + List findByBusRouteIds(Collection routeIds); /** - * Retrieves route statuses for the specified route ID. + * Retrieves route statuses for the specified bus route ID. * - * @param routeId the route ID - * @return a {@link List} of {@link RouteStatus}es associated with the route ID, or an empty {@link List} if no - * route statuses are found for the route ID + * @param routeId the bus route ID + * @return a {@link List} of {@link RouteStatus}es associated with the bus route ID, or an empty {@link List} if + * no route statuses are found for the bus route ID * @throws NullPointerException if {@code routeId} is {@code null} * @throws Cta4jRouteStatusException if the API returns an error response or the response cannot be parsed */ - default List findByRouteId(String routeId) { + default List findByBusRouteId(String routeId) { Objects.requireNonNull(routeId); List routeIds = List.of(routeId); - return this.findByRouteIds(routeIds); + return this.findByBusRouteIds(routeIds); + } + + /** + * Retrieves route statuses for the specified train lines. + * + * @param lines a {@link Collection} of train lines + * @return a {@link List} of {@link TrainRouteStatus}es associated with the train lines, or an empty {@link List} + * if no route statuses are found for the train lines + * @throws NullPointerException if {@code lines} is {@code null} or contains {@code null} elements + * @throws Cta4jRouteStatusException if the API returns an error response or the response cannot be parsed + */ + List findByLines(Collection lines); + + /** + * Retrieves route statuses for the specified train line. + * + * @param line the train line + * @return a {@link List} of {@link TrainRouteStatus}es associated with the train line, or an empty {@link List} + * if no route statuses are found for the train line + * @throws NullPointerException if {@code line} is {@code null} + * @throws Cta4jRouteStatusException if the API returns an error response or the response cannot be parsed + */ + default List findByLine(TrainLine line) { + Objects.requireNonNull(line); + + List lines = List.of(line); + + return this.findByLines(lines); } /** diff --git a/src/main/java/com/cta4j/alert/routestatus/internal/impl/RouteStatusApiImpl.java b/src/main/java/com/cta4j/alert/routestatus/internal/impl/RouteStatusApiImpl.java index b0b9b6f1..e6d308f1 100644 --- a/src/main/java/com/cta4j/alert/routestatus/internal/impl/RouteStatusApiImpl.java +++ b/src/main/java/com/cta4j/alert/routestatus/internal/impl/RouteStatusApiImpl.java @@ -11,6 +11,8 @@ import com.cta4j.alert.routestatus.internal.wire.CtaRoutes; import com.cta4j.alert.routestatus.model.RouteStatus; import com.cta4j.alert.routestatus.model.ServiceType; +import com.cta4j.alert.routestatus.model.TrainRouteStatus; +import com.cta4j.common.train.TrainLine; import org.apache.hc.client5.http.fluent.Request; import org.apache.hc.core5.net.URIBuilder; import org.jetbrains.annotations.ApiStatus; @@ -19,9 +21,11 @@ import tools.jackson.databind.json.JsonMapper; import java.io.IOException; +import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Objects; +import java.util.stream.Collectors; @ApiStatus.Internal @NullMarked @@ -51,15 +55,52 @@ public List findByTypes(Collection types) { List typesList = List.copyOf(types); - return List.of(); + if (typesList.isEmpty()) { + return List.of(); + } + + String typesString = typesList.stream() + .map(ServiceType::name) + .map(String::toLowerCase) + .collect(Collectors.joining(",")); + + String url = new URIBuilder() + .setScheme(this.config.scheme()) + .setHost(this.config.host()) + .setPort(this.config.port()) + .setPath(AlertApiConstants.ROUTE_STATUS_ENDPOINT) + .addParameter("type", typesString) + .addParameter("outputType", "JSON") + .toString(); + + return this.makeRequest(url); } @Override - public List findByRouteIds(Collection routeIds) { + public List findByBusRouteIds(Collection routeIds) { Objects.requireNonNull(routeIds); List routeIdsList = List.copyOf(routeIds); + if (routeIdsList.isEmpty()) { + return List.of(); + } + + for (String routeId : routeIdsList) { + if (isTrainLine(routeId)) { + String message = """ + %s is a train line, not a bus route; \ + use findByLines(Collection) instead""".formatted(routeId); + + throw new IllegalArgumentException(message); + } + } + + return List.of(); + } + + @Override + public List findByLines(Collection lines) { return List.of(); } @@ -125,4 +166,10 @@ private List makeRequest(String url) { throw new Cta4jRouteStatusException(message, integerCode); } + + private static boolean isTrainLine(String routeId) { + return Arrays.stream(TrainLine.values()) + .map(TrainLine::getCode) + .anyMatch(code -> code.equalsIgnoreCase(routeId)); + } } diff --git a/src/main/java/com/cta4j/alert/routestatus/model/RouteStatus.java b/src/main/java/com/cta4j/alert/routestatus/model/RouteStatus.java index 43a9f41e..701ae737 100644 --- a/src/main/java/com/cta4j/alert/routestatus/model/RouteStatus.java +++ b/src/main/java/com/cta4j/alert/routestatus/model/RouteStatus.java @@ -8,14 +8,14 @@ /** * Represents the service status of a single route. * - * @param route the name of this route (e.g., "Red Line") - * @param color the color of this route used in maps, as {@code rrggbb} (e.g., "c60c30") + * @param route the name of this route (e.g., "Clark") + * @param color the color of this route used in maps, as {@code rrggbb} (e.g., "565a5c") * @param textColor the suggested color of text displayed against {@code color}, as {@code rrggbb} (e.g., "ffffff") - * @param serviceId the unique GTFS route or station identifier of this route (e.g., "Red") + * @param serviceId the unique GTFS route or station identifier of this route (e.g., "22") * @param url the URL of this route's or station's page on transitchicago.com - * @param status the ultimate, human-readable status of this route (e.g., "Normal service", "Planned work", "Minor - * delays") - * @param statusColor the suggested color associated with {@code status}, as {@code rrggbb} (e.g., "404040") + * @param status the ultimate, human-readable status of this route (e.g., "Normal Service", "Service Change", + * "Bus Stop Note") + * @param statusColor the suggested color associated with {@code status}, as {@code rrggbb} (e.g., "000000") */ @NullMarked public record RouteStatus( @@ -30,15 +30,14 @@ public record RouteStatus( /** * Constructs a {@code RouteStatus}. * - * @param route the name of the route (e.g., "Red Line") - * @param color the color of the route used in maps, as {@code rrggbb} (e.g., "c60c30") - * @param textColor the suggested color of text displayed against {@code color}, as {@code rrggbb} (e.g., - * "ffffff") - * @param serviceId the unique GTFS route or station identifier of the route (e.g., "Red") + * @param route the name of the route (e.g., "Clark") + * @param color the color of the route used in maps, as {@code rrggbb} (e.g., "565a5c") + * @param textColor the suggested color of text displayed against {@code color}, as {@code rrggbb} (e.g., "ffffff") + * @param serviceId the unique GTFS route or station identifier of the route (e.g., "22") * @param url the URL of the route's or station's page on transitchicago.com - * @param status the ultimate, human-readable status of the route (e.g., "Normal service", "Planned work", - * "Minor delays") - * @param statusColor the suggested color associated with {@code status}, as {@code rrggbb} (e.g., "404040") + * @param status the ultimate, human-readable status of the route (e.g., "Normal Service", "Service Change", + * "Bus Stop Note") + * @param statusColor the suggested color associated with {@code status}, as {@code rrggbb} (e.g., "000000") * @throws NullPointerException if {@code route}, {@code color}, {@code textColor}, {@code serviceId}, * {@code url}, {@code status}, or {@code statusColor} is {@code null} */ @@ -51,4 +50,4 @@ public record RouteStatus( Objects.requireNonNull(status); Objects.requireNonNull(statusColor); } -} \ No newline at end of file +} diff --git a/src/main/java/com/cta4j/alert/routestatus/model/TrainRouteStatus.java b/src/main/java/com/cta4j/alert/routestatus/model/TrainRouteStatus.java new file mode 100644 index 00000000..66ec9cfc --- /dev/null +++ b/src/main/java/com/cta4j/alert/routestatus/model/TrainRouteStatus.java @@ -0,0 +1,52 @@ +package com.cta4j.alert.routestatus.model; + +import com.cta4j.common.train.TrainLine; +import org.jspecify.annotations.NullMarked; + +import java.net.URI; +import java.util.Objects; + +/** + * Represents the service status of a single train route. + * + * @param route the name of this route (e.g., "Red Line") + * @param color the color of this route used in maps, as {@code rrggbb} (e.g., "c60c30") + * @param textColor the suggested color of text displayed against {@code color}, as {@code rrggbb} (e.g., "ffffff") + * @param line the {@link TrainLine} this status corresponds to + * @param url the URL of this route's page on transitchicago.com + * @param status the ultimate, human-readable status of this route (e.g., "Normal Service", "Service Change") + * @param statusColor the suggested color associated with {@code status}, as {@code rrggbb} (e.g., "404040") + */ +@NullMarked +public record TrainRouteStatus( + String route, + String color, + String textColor, + TrainLine line, + URI url, + String status, + String statusColor +) { + /** + * Constructs a {@code TrainRouteStatus}. + * + * @param route the name of the route (e.g., "Red Line") + * @param color the color of the route used in maps, as {@code rrggbb} (e.g., "c60c30") + * @param textColor the suggested color of text displayed against {@code color}, as {@code rrggbb} (e.g., "ffffff") + * @param line the {@link TrainLine} the route corresponds to + * @param url the URL of the route's page on transitchicago.com + * @param status the ultimate, human-readable status of the route (e.g., "Normal Service", "Service Change") + * @param statusColor the suggested color associated with {@code status}, as {@code rrggbb} (e.g., "404040") + * @throws NullPointerException if {@code route}, {@code color}, {@code textColor}, {@code line}, + * {@code url}, {@code status}, or {@code statusColor} is {@code null} + */ + public TrainRouteStatus { + Objects.requireNonNull(route); + Objects.requireNonNull(color); + Objects.requireNonNull(textColor); + Objects.requireNonNull(line); + Objects.requireNonNull(url); + Objects.requireNonNull(status); + Objects.requireNonNull(statusColor); + } +} diff --git a/src/main/java/com/cta4j/train/common/model/TrainLine.java b/src/main/java/com/cta4j/common/train/TrainLine.java similarity index 98% rename from src/main/java/com/cta4j/train/common/model/TrainLine.java rename to src/main/java/com/cta4j/common/train/TrainLine.java index 252e7687..59244e03 100644 --- a/src/main/java/com/cta4j/train/common/model/TrainLine.java +++ b/src/main/java/com/cta4j/common/train/TrainLine.java @@ -1,4 +1,4 @@ -package com.cta4j.train.common.model; +package com.cta4j.common.train; import org.jspecify.annotations.NullMarked; diff --git a/src/main/java/com/cta4j/train/arrival/internal/impl/ArrivalsApiImpl.java b/src/main/java/com/cta4j/train/arrival/internal/impl/ArrivalsApiImpl.java index f8267d73..603b4f33 100644 --- a/src/main/java/com/cta4j/train/arrival/internal/impl/ArrivalsApiImpl.java +++ b/src/main/java/com/cta4j/train/arrival/internal/impl/ArrivalsApiImpl.java @@ -12,7 +12,7 @@ import com.cta4j.train.common.internal.wire.CtaArrival; import com.cta4j.train.common.internal.wire.CtaResponse; import com.cta4j.train.common.model.Arrival; -import com.cta4j.train.common.model.TrainLine; +import com.cta4j.common.train.TrainLine; import org.apache.hc.client5.http.fluent.Request; import org.apache.hc.core5.net.URIBuilder; import org.jetbrains.annotations.ApiStatus; diff --git a/src/main/java/com/cta4j/train/arrival/query/MapArrivalQuery.java b/src/main/java/com/cta4j/train/arrival/query/MapArrivalQuery.java index 2ea87f7e..7c44b191 100644 --- a/src/main/java/com/cta4j/train/arrival/query/MapArrivalQuery.java +++ b/src/main/java/com/cta4j/train/arrival/query/MapArrivalQuery.java @@ -1,6 +1,6 @@ package com.cta4j.train.arrival.query; -import com.cta4j.train.common.model.TrainLine; +import com.cta4j.common.train.TrainLine; import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; diff --git a/src/main/java/com/cta4j/train/arrival/query/StopArrivalQuery.java b/src/main/java/com/cta4j/train/arrival/query/StopArrivalQuery.java index e0c9e917..e0796226 100644 --- a/src/main/java/com/cta4j/train/arrival/query/StopArrivalQuery.java +++ b/src/main/java/com/cta4j/train/arrival/query/StopArrivalQuery.java @@ -1,6 +1,6 @@ package com.cta4j.train.arrival.query; -import com.cta4j.train.common.model.TrainLine; +import com.cta4j.common.train.TrainLine; import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; diff --git a/src/main/java/com/cta4j/train/common/internal/mapper/Qualifiers.java b/src/main/java/com/cta4j/train/common/internal/mapper/Qualifiers.java index 39c6a3b7..d42a3856 100644 --- a/src/main/java/com/cta4j/train/common/internal/mapper/Qualifiers.java +++ b/src/main/java/com/cta4j/train/common/internal/mapper/Qualifiers.java @@ -3,7 +3,7 @@ import com.cta4j.common.geo.Coordinates; import com.cta4j.train.common.internal.wire.CtaArrival; import com.cta4j.train.common.model.TrainDirection; -import com.cta4j.train.common.model.TrainLine; +import com.cta4j.common.train.TrainLine; import com.cta4j.train.follow.internal.wire.CtaPosition; import com.cta4j.train.station.internal.wire.CtaStation; import com.cta4j.train.station.model.CardinalDirection; diff --git a/src/main/java/com/cta4j/train/common/model/Arrival.java b/src/main/java/com/cta4j/train/common/model/Arrival.java index 3b4eab09..d979553e 100644 --- a/src/main/java/com/cta4j/train/common/model/Arrival.java +++ b/src/main/java/com/cta4j/train/common/model/Arrival.java @@ -1,5 +1,6 @@ package com.cta4j.train.common.model; +import com.cta4j.common.train.TrainLine; import org.jspecify.annotations.NullMarked; import java.time.Instant; diff --git a/src/main/java/com/cta4j/train/location/LocationsApi.java b/src/main/java/com/cta4j/train/location/LocationsApi.java index b24c1046..934e25e4 100644 --- a/src/main/java/com/cta4j/train/location/LocationsApi.java +++ b/src/main/java/com/cta4j/train/location/LocationsApi.java @@ -1,6 +1,6 @@ package com.cta4j.train.location; -import com.cta4j.train.common.model.TrainLine; +import com.cta4j.common.train.TrainLine; import com.cta4j.train.location.exception.Cta4jLocationsException; import com.cta4j.train.location.model.TrainLocations; import org.jspecify.annotations.NullMarked; diff --git a/src/main/java/com/cta4j/train/location/internal/impl/LocationsApiImpl.java b/src/main/java/com/cta4j/train/location/internal/impl/LocationsApiImpl.java index bb7a59e8..c17d18d2 100644 --- a/src/main/java/com/cta4j/train/location/internal/impl/LocationsApiImpl.java +++ b/src/main/java/com/cta4j/train/location/internal/impl/LocationsApiImpl.java @@ -3,7 +3,7 @@ import com.cta4j.train.common.internal.config.TrainApiConfig; import com.cta4j.train.common.internal.util.TrainApiConstants; import com.cta4j.train.common.internal.wire.CtaResponse; -import com.cta4j.train.common.model.TrainLine; +import com.cta4j.common.train.TrainLine; import com.cta4j.train.location.LocationsApi; import com.cta4j.train.location.exception.Cta4jLocationsException; import com.cta4j.train.location.exception.LocationsErrorCode; diff --git a/src/main/java/com/cta4j/train/location/model/TrainLocations.java b/src/main/java/com/cta4j/train/location/model/TrainLocations.java index 5629cf27..f1dfc739 100644 --- a/src/main/java/com/cta4j/train/location/model/TrainLocations.java +++ b/src/main/java/com/cta4j/train/location/model/TrainLocations.java @@ -1,6 +1,6 @@ package com.cta4j.train.location.model; -import com.cta4j.train.common.model.TrainLine; +import com.cta4j.common.train.TrainLine; import org.jspecify.annotations.NullMarked; import java.util.List; diff --git a/src/main/java/com/cta4j/train/station/model/Station.java b/src/main/java/com/cta4j/train/station/model/Station.java index a8396cc4..00cdd3a9 100644 --- a/src/main/java/com/cta4j/train/station/model/Station.java +++ b/src/main/java/com/cta4j/train/station/model/Station.java @@ -1,6 +1,6 @@ package com.cta4j.train.station.model; -import com.cta4j.train.common.model.TrainLine; +import com.cta4j.common.train.TrainLine; import org.jspecify.annotations.NullMarked; import java.util.Objects; diff --git a/src/test/java/com/cta4j/train/arrival/ArrivalMapperTest.java b/src/test/java/com/cta4j/train/arrival/ArrivalMapperTest.java index 71ab7c35..f7ee6f18 100644 --- a/src/test/java/com/cta4j/train/arrival/ArrivalMapperTest.java +++ b/src/test/java/com/cta4j/train/arrival/ArrivalMapperTest.java @@ -3,7 +3,7 @@ import com.cta4j.train.common.internal.mapper.ArrivalMapper; import com.cta4j.train.common.internal.wire.CtaArrival; import com.cta4j.train.common.model.Arrival; -import com.cta4j.train.common.model.TrainLine; +import com.cta4j.common.train.TrainLine; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.*; diff --git a/src/test/java/com/cta4j/train/arrival/ArrivalsApiImplTest.java b/src/test/java/com/cta4j/train/arrival/ArrivalsApiImplTest.java index 020b481d..25692a66 100644 --- a/src/test/java/com/cta4j/train/arrival/ArrivalsApiImplTest.java +++ b/src/test/java/com/cta4j/train/arrival/ArrivalsApiImplTest.java @@ -8,7 +8,7 @@ import com.cta4j.train.arrival.query.StopArrivalQuery; import com.cta4j.train.common.internal.config.TrainApiConfig; import com.cta4j.train.common.model.Arrival; -import com.cta4j.train.common.model.TrainLine; +import com.cta4j.common.train.TrainLine; import com.github.tomakehurst.wiremock.WireMockServer; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; diff --git a/src/test/java/com/cta4j/train/arrival/query/MapArrivalQueryTest.java b/src/test/java/com/cta4j/train/arrival/query/MapArrivalQueryTest.java index 11204314..4befcdb3 100644 --- a/src/test/java/com/cta4j/train/arrival/query/MapArrivalQueryTest.java +++ b/src/test/java/com/cta4j/train/arrival/query/MapArrivalQueryTest.java @@ -1,6 +1,6 @@ package com.cta4j.train.arrival.query; -import com.cta4j.train.common.model.TrainLine; +import com.cta4j.common.train.TrainLine; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.*; diff --git a/src/test/java/com/cta4j/train/arrival/query/StopArrivalQueryTest.java b/src/test/java/com/cta4j/train/arrival/query/StopArrivalQueryTest.java index 0df441aa..e78ac8f9 100644 --- a/src/test/java/com/cta4j/train/arrival/query/StopArrivalQueryTest.java +++ b/src/test/java/com/cta4j/train/arrival/query/StopArrivalQueryTest.java @@ -1,6 +1,6 @@ package com.cta4j.train.arrival.query; -import com.cta4j.train.common.model.TrainLine; +import com.cta4j.common.train.TrainLine; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.*; diff --git a/src/test/java/com/cta4j/train/common/TrainQualifiersTest.java b/src/test/java/com/cta4j/train/common/TrainQualifiersTest.java index 3bafa7d1..e9d5512b 100644 --- a/src/test/java/com/cta4j/train/common/TrainQualifiersTest.java +++ b/src/test/java/com/cta4j/train/common/TrainQualifiersTest.java @@ -4,7 +4,7 @@ import com.cta4j.train.common.internal.mapper.Qualifiers; import com.cta4j.train.common.internal.wire.CtaArrival; import com.cta4j.train.common.model.TrainDirection; -import com.cta4j.train.common.model.TrainLine; +import com.cta4j.common.train.TrainLine; import com.cta4j.train.follow.internal.wire.CtaPosition; import com.cta4j.train.station.internal.wire.CtaLocation; import com.cta4j.train.station.internal.wire.CtaStation; diff --git a/src/test/java/com/cta4j/train/common/model/TrainLineTest.java b/src/test/java/com/cta4j/train/common/model/TrainLineTest.java index 3c33a3ff..3fb51290 100644 --- a/src/test/java/com/cta4j/train/common/model/TrainLineTest.java +++ b/src/test/java/com/cta4j/train/common/model/TrainLineTest.java @@ -1,5 +1,6 @@ package com.cta4j.train.common.model; +import com.cta4j.common.train.TrainLine; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.*; diff --git a/src/test/java/com/cta4j/train/location/LocationsApiImplTest.java b/src/test/java/com/cta4j/train/location/LocationsApiImplTest.java index a8c4ffdb..ba1d1085 100644 --- a/src/test/java/com/cta4j/train/location/LocationsApiImplTest.java +++ b/src/test/java/com/cta4j/train/location/LocationsApiImplTest.java @@ -2,7 +2,7 @@ import com.cta4j.TestFixtures; import com.cta4j.train.common.internal.config.TrainApiConfig; -import com.cta4j.train.common.model.TrainLine; +import com.cta4j.common.train.TrainLine; import com.cta4j.train.location.exception.Cta4jLocationsException; import com.cta4j.train.location.exception.LocationsErrorCode; import com.cta4j.train.location.internal.impl.LocationsApiImpl; diff --git a/src/test/java/com/cta4j/train/location/TrainLocationsMapperTest.java b/src/test/java/com/cta4j/train/location/TrainLocationsMapperTest.java index edb5ba14..a0c4feef 100644 --- a/src/test/java/com/cta4j/train/location/TrainLocationsMapperTest.java +++ b/src/test/java/com/cta4j/train/location/TrainLocationsMapperTest.java @@ -1,6 +1,6 @@ package com.cta4j.train.location; -import com.cta4j.train.common.model.TrainLine; +import com.cta4j.common.train.TrainLine; import com.cta4j.train.location.internal.mapper.TrainLocationsMapper; import com.cta4j.train.location.internal.wire.CtaLocationTrain; import com.cta4j.train.location.internal.wire.CtaRoute; diff --git a/src/test/java/com/cta4j/train/station/StationMapperTest.java b/src/test/java/com/cta4j/train/station/StationMapperTest.java index cf0be6e5..5850ef97 100644 --- a/src/test/java/com/cta4j/train/station/StationMapperTest.java +++ b/src/test/java/com/cta4j/train/station/StationMapperTest.java @@ -1,6 +1,6 @@ package com.cta4j.train.station; -import com.cta4j.train.common.model.TrainLine; +import com.cta4j.common.train.TrainLine; import com.cta4j.train.station.internal.mapper.StationMapper; import com.cta4j.train.station.internal.wire.CtaLocation; import com.cta4j.train.station.internal.wire.CtaStation; diff --git a/src/test/java/com/cta4j/train/station/StationsApiImplTest.java b/src/test/java/com/cta4j/train/station/StationsApiImplTest.java index a1d7f6c1..9e14d760 100644 --- a/src/test/java/com/cta4j/train/station/StationsApiImplTest.java +++ b/src/test/java/com/cta4j/train/station/StationsApiImplTest.java @@ -3,7 +3,7 @@ import com.cta4j.TestFixtures; import com.cta4j.train.common.exception.Cta4jTrainException; import com.cta4j.train.common.internal.config.TrainApiConfig; -import com.cta4j.train.common.model.TrainLine; +import com.cta4j.common.train.TrainLine; import com.cta4j.train.station.internal.impl.StationsApiImpl; import com.cta4j.train.station.model.Station; import com.github.tomakehurst.wiremock.WireMockServer; From 4518c76bf8fe970f29dcb14d6d13c082c3469946 Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Fri, 17 Jul 2026 14:38:46 -0500 Subject: [PATCH 06/60] Route status API --- src/main/java/com/cta4j/alert/AlertApi.java | 47 +++++++++ .../internal/config/AlertApiConfig.java | 8 +- .../common/internal/impl/AlertApiImpl.java | 54 +++++++++++ .../common/internal/mapper/Qualifiers.java | 16 +++- .../internal/impl/RouteStatusApiImpl.java | 96 ++++++++++++++++--- .../mapper/TrainRouteStatusMapper.java | 23 +++++ .../routestatus/internal/wire/CtaRoutes.java | 20 +++- .../alert/routestatus/model/ServiceType.java | 18 ++++ 8 files changed, 262 insertions(+), 20 deletions(-) create mode 100644 src/main/java/com/cta4j/alert/common/internal/impl/AlertApiImpl.java create mode 100644 src/main/java/com/cta4j/alert/routestatus/internal/mapper/TrainRouteStatusMapper.java diff --git a/src/main/java/com/cta4j/alert/AlertApi.java b/src/main/java/com/cta4j/alert/AlertApi.java index ce1b97b7..d8b8dd3c 100644 --- a/src/main/java/com/cta4j/alert/AlertApi.java +++ b/src/main/java/com/cta4j/alert/AlertApi.java @@ -1,9 +1,56 @@ package com.cta4j.alert; +import com.cta4j.alert.common.internal.impl.AlertApiImpl; import com.cta4j.alert.routestatus.RouteStatusApi; import org.jspecify.annotations.NullMarked; +/** + * Primary entry point for interacting with the CTA Alerts API. + *

+ * This interface provides grouped sub-APIs for different aspects of the CTA Alerts API, such as route status and + * detailed alerts. + *

+ * Instances of {@code AlertApi} are immutable and thread-safe once built. + * Use {@link #builder()} to construct a configured instance. + */ @NullMarked public interface AlertApi { + /** + * Provides access to route status-related endpoints. + * + * @return the {@link RouteStatusApi} + */ RouteStatusApi routeStatus(); + + /** + * Builder for constructing {@link AlertApi} instances. + */ + interface Builder { + /** + * Sets the API host to use for requests. + *

+ * If not specified, the default CTA Alerts API host is used. + * + * @param host the API host + * @return this builder instance + * @throws NullPointerException if {@code host} is {@code null} + */ + Builder host(String host); + + /** + * Builds a configured {@link AlertApi} instance. + * + * @return a new {@link AlertApi} + */ + AlertApi build(); + } + + /** + * Creates a new {@link Builder} for constructing a {@link AlertApi}. + * + * @return a new {@link Builder} + */ + static Builder builder() { + return new AlertApiImpl.BuilderImpl(); + } } diff --git a/src/main/java/com/cta4j/alert/common/internal/config/AlertApiConfig.java b/src/main/java/com/cta4j/alert/common/internal/config/AlertApiConfig.java index a7f10ce2..ef443a74 100644 --- a/src/main/java/com/cta4j/alert/common/internal/config/AlertApiConfig.java +++ b/src/main/java/com/cta4j/alert/common/internal/config/AlertApiConfig.java @@ -10,16 +10,14 @@ public record AlertApiConfig( String scheme, String host, - int port, - String apiKey + int port ) { public AlertApiConfig { Objects.requireNonNull(scheme); Objects.requireNonNull(host); - Objects.requireNonNull(apiKey); } - public AlertApiConfig(String scheme, String host, String apiKey) { - this(scheme, host, -1, apiKey); + public AlertApiConfig(String scheme, String host) { + this(scheme, host, -1); } } diff --git a/src/main/java/com/cta4j/alert/common/internal/impl/AlertApiImpl.java b/src/main/java/com/cta4j/alert/common/internal/impl/AlertApiImpl.java new file mode 100644 index 00000000..a6ed0561 --- /dev/null +++ b/src/main/java/com/cta4j/alert/common/internal/impl/AlertApiImpl.java @@ -0,0 +1,54 @@ +package com.cta4j.alert.common.internal.impl; + +import com.cta4j.alert.AlertApi; +import com.cta4j.alert.common.internal.config.AlertApiConfig; +import com.cta4j.alert.common.internal.util.AlertApiConstants; +import com.cta4j.alert.routestatus.RouteStatusApi; +import com.cta4j.alert.routestatus.internal.impl.RouteStatusApiImpl; +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +import java.util.Objects; + +@ApiStatus.Internal +@NullMarked +public final class AlertApiImpl implements AlertApi { + private final RouteStatusApi routeStatusApi; + + public AlertApiImpl(AlertApiConfig config) { + Objects.requireNonNull(config); + + this.routeStatusApi = new RouteStatusApiImpl(config); + } + + @Override + public RouteStatusApi routeStatus() { + return this.routeStatusApi; + } + + public static final class BuilderImpl implements AlertApi.Builder { + @Nullable + private String host; + + public BuilderImpl() { + this.host = null; + } + + @Override + public Builder host(String host) { + this.host = Objects.requireNonNull(host); + + return this; + } + + @Override + public AlertApi build() { + String finalHost = Objects.requireNonNullElse(this.host, AlertApiConstants.DEFAULT_HOST); + + AlertApiConfig config = new AlertApiConfig(AlertApiConstants.SCHEME, finalHost); + + return new AlertApiImpl(config); + } + } +} diff --git a/src/main/java/com/cta4j/alert/common/internal/mapper/Qualifiers.java b/src/main/java/com/cta4j/alert/common/internal/mapper/Qualifiers.java index eda760b4..9e11ea36 100644 --- a/src/main/java/com/cta4j/alert/common/internal/mapper/Qualifiers.java +++ b/src/main/java/com/cta4j/alert/common/internal/mapper/Qualifiers.java @@ -1,5 +1,6 @@ package com.cta4j.alert.common.internal.mapper; +import com.cta4j.common.train.TrainLine; import org.jetbrains.annotations.ApiStatus; import org.jspecify.annotations.NullMarked; import org.mapstruct.Named; @@ -27,4 +28,17 @@ public static URI mapUri(String value) { throw new IllegalArgumentException(message, e); } } -} \ No newline at end of file + + @Named("mapTrainLine") + public static TrainLine mapTrainLine(String code) { + Objects.requireNonNull(code); + + try { + return TrainLine.fromCode(code); + } catch (IllegalArgumentException e) { + String message = "Failed to parse train line code: %s".formatted(code); + + throw new IllegalArgumentException(message, e); + } + } +} diff --git a/src/main/java/com/cta4j/alert/routestatus/internal/impl/RouteStatusApiImpl.java b/src/main/java/com/cta4j/alert/routestatus/internal/impl/RouteStatusApiImpl.java index e6d308f1..51f4a800 100644 --- a/src/main/java/com/cta4j/alert/routestatus/internal/impl/RouteStatusApiImpl.java +++ b/src/main/java/com/cta4j/alert/routestatus/internal/impl/RouteStatusApiImpl.java @@ -6,6 +6,7 @@ import com.cta4j.alert.routestatus.exception.Cta4jRouteStatusException; import com.cta4j.alert.routestatus.exception.RouteStatusErrorCode; import com.cta4j.alert.routestatus.internal.mapper.RouteStatusMapper; +import com.cta4j.alert.routestatus.internal.mapper.TrainRouteStatusMapper; import com.cta4j.alert.routestatus.internal.wire.CtaRouteInfo; import com.cta4j.alert.routestatus.internal.wire.CtaRouteStatusResponse; import com.cta4j.alert.routestatus.internal.wire.CtaRoutes; @@ -17,6 +18,9 @@ import org.apache.hc.core5.net.URIBuilder; import org.jetbrains.annotations.ApiStatus; import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import tools.jackson.core.JacksonException; import tools.jackson.databind.json.JsonMapper; @@ -25,11 +29,14 @@ import java.util.Collection; import java.util.List; import java.util.Objects; +import java.util.function.Function; import java.util.stream.Collectors; @ApiStatus.Internal @NullMarked public final class RouteStatusApiImpl implements RouteStatusApi { + private static final Logger log = LoggerFactory.getLogger(RouteStatusApiImpl.class); + private final AlertApiConfig config; public RouteStatusApiImpl(AlertApiConfig config) { @@ -46,7 +53,7 @@ public List list() { .addParameter("outputType", "JSON") .toString(); - return this.makeRequest(url); + return this.makeRequest(url, RouteStatusMapper.INSTANCE::toDomain); } @Override @@ -73,7 +80,7 @@ public List findByTypes(Collection types) { .addParameter("outputType", "JSON") .toString(); - return this.makeRequest(url); + return this.makeRequest(url, RouteStatusMapper.INSTANCE::toDomain); } @Override @@ -96,22 +103,63 @@ public List findByBusRouteIds(Collection routeIds) { } } - return List.of(); + String routeIdsString = String.join(",", routeIdsList); + + String url = new URIBuilder() + .setScheme(this.config.scheme()) + .setHost(this.config.host()) + .setPort(this.config.port()) + .setPath(AlertApiConstants.ROUTE_STATUS_ENDPOINT) + .addParameter("routeid", routeIdsString) + .addParameter("outputType", "JSON") + .toString(); + + return this.makeRequest(url, RouteStatusMapper.INSTANCE::toDomain); } @Override public List findByLines(Collection lines) { - return List.of(); + Objects.requireNonNull(lines); + + List linesList = List.copyOf(lines); + + if (linesList.isEmpty()) { + return List.of(); + } + + String linesString = linesList.stream() + .map(TrainLine::getCode) + .collect(Collectors.joining(",")); + + String url = new URIBuilder() + .setScheme(this.config.scheme()) + .setHost(this.config.host()) + .setPort(this.config.port()) + .setPath(AlertApiConstants.ROUTE_STATUS_ENDPOINT) + .addParameter("routeid", linesString) + .addParameter("outputType", "JSON") + .toString(); + + return this.makeRequest(url, TrainRouteStatusMapper.INSTANCE::toDomain); } @Override public List findByStationId(String stationId) { Objects.requireNonNull(stationId); - return List.of(); + String url = new URIBuilder() + .setScheme(this.config.scheme()) + .setHost(this.config.host()) + .setPort(this.config.port()) + .setPath(AlertApiConstants.ROUTE_STATUS_ENDPOINT) + .addParameter("stationid", stationId) + .addParameter("outputType", "JSON") + .toString(); + + return this.makeRequest(url, RouteStatusMapper.INSTANCE::toDomain); } - private List makeRequest(String url) { + private List makeRequest(String url, Function mapper) { String response; try { @@ -140,14 +188,36 @@ private List makeRequest(String url) { if (routeInfo != null && !routeInfo.isEmpty()) { return routeInfo.stream() - .map(RouteStatusMapper.INSTANCE::toDomain) + .map(mapper) .toList(); } + List errorCodeStrings = ctaRoutes.errorCode(); + + if (errorCodeStrings == null || errorCodeStrings.isEmpty()) { + log.warn("Received empty response from {}", AlertApiConstants.ROUTE_STATUS_ENDPOINT); + + return List.of(); + } + + long distinctCount = errorCodeStrings.stream() + .distinct() + .count(); + + if (distinctCount > 1L) { + log.warn( + "Received multiple distinct error codes from {}: {}", + AlertApiConstants.ROUTE_STATUS_ENDPOINT, + errorCodeStrings + ); + } + + String errorCodeString = errorCodeStrings.getFirst(); + int integerCode; try { - integerCode = Integer.parseInt(ctaRoutes.errorCode()); + integerCode = Integer.parseInt(errorCodeString); } catch (NumberFormatException e) { throw new Cta4jRouteStatusException("Failed to parse error code", e); } @@ -158,11 +228,15 @@ private List makeRequest(String url) { return List.of(); } - String errorMessage = ctaRoutes.errorMessage(); + List<@Nullable String> errorMessages = ctaRoutes.errorMessage(); - String message = errorMessage == null || errorMessage.isBlank() + String message = errorMessages == null || errorMessages.isEmpty() ? "An unknown error occurred." - : errorMessage; + : errorMessages.getFirst(); + + if (message == null || message.isBlank()) { + message = "An unknown error occurred."; + } throw new Cta4jRouteStatusException(message, integerCode); } diff --git a/src/main/java/com/cta4j/alert/routestatus/internal/mapper/TrainRouteStatusMapper.java b/src/main/java/com/cta4j/alert/routestatus/internal/mapper/TrainRouteStatusMapper.java new file mode 100644 index 00000000..fedb6b49 --- /dev/null +++ b/src/main/java/com/cta4j/alert/routestatus/internal/mapper/TrainRouteStatusMapper.java @@ -0,0 +1,23 @@ +package com.cta4j.alert.routestatus.internal.mapper; + +import com.cta4j.alert.common.internal.mapper.Qualifiers; +import com.cta4j.alert.routestatus.internal.wire.CtaRouteInfo; +import com.cta4j.alert.routestatus.model.TrainRouteStatus; +import org.jetbrains.annotations.ApiStatus; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +@Mapper(uses = Qualifiers.class) +@ApiStatus.Internal +public interface TrainRouteStatusMapper { + TrainRouteStatusMapper INSTANCE = Mappers.getMapper(TrainRouteStatusMapper.class); + + @Mapping(target = "color", source = "routeColorCode") + @Mapping(target = "textColor", source = "routeTextColor") + @Mapping(target = "url", source = "routeUrl.cdataSection", qualifiedByName = "mapUri") + @Mapping(target = "line", source = "serviceId", qualifiedByName = "mapTrainLine") + @Mapping(target = "status", source = "routeStatus") + @Mapping(target = "statusColor", source = "routeStatusColor") + TrainRouteStatus toDomain(CtaRouteInfo routeInfo); +} diff --git a/src/main/java/com/cta4j/alert/routestatus/internal/wire/CtaRoutes.java b/src/main/java/com/cta4j/alert/routestatus/internal/wire/CtaRoutes.java index 21b8a141..c5163f46 100644 --- a/src/main/java/com/cta4j/alert/routestatus/internal/wire/CtaRoutes.java +++ b/src/main/java/com/cta4j/alert/routestatus/internal/wire/CtaRoutes.java @@ -7,6 +7,8 @@ import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Objects; @@ -18,11 +20,14 @@ public record CtaRoutes( String timestamp, @JsonProperty("ErrorCode") - String errorCode, + @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY) + @Nullable + List errorCode, @JsonProperty("ErrorMessage") + @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY) @Nullable - String errorMessage, + List<@Nullable String> errorMessage, @JsonProperty("RouteInfo") @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY) @@ -31,7 +36,16 @@ public record CtaRoutes( ) { public CtaRoutes { Objects.requireNonNull(timestamp); - Objects.requireNonNull(errorCode); + + if (errorCode != null) { + errorCode = List.copyOf(errorCode); + } + + if (errorMessage != null) { + errorMessage = new ArrayList<>(errorMessage); + + errorMessage = Collections.unmodifiableList(errorMessage); + } if (routeInfo != null) { routeInfo = List.copyOf(routeInfo); diff --git a/src/main/java/com/cta4j/alert/routestatus/model/ServiceType.java b/src/main/java/com/cta4j/alert/routestatus/model/ServiceType.java index 1e6acb3a..7707d449 100644 --- a/src/main/java/com/cta4j/alert/routestatus/model/ServiceType.java +++ b/src/main/java/com/cta4j/alert/routestatus/model/ServiceType.java @@ -2,10 +2,28 @@ import org.jspecify.annotations.NullMarked; +/** + * Represents the type of service to filter route statuses by. + */ @NullMarked public enum ServiceType { + /** + * Indicates bus routes. + */ BUS, + + /** + * Indicates rail (train) routes. + */ RAIL, + + /** + * Indicates train stations. + */ STATION, + + /** + * Indicates systemwide categories, such as all routes, all bus routes, or all train routes. + */ SYSTEMWIDE } From d78e278dddd9aabd22113bf53de3165e4d08b9e0 Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Sat, 18 Jul 2026 11:55:25 -0500 Subject: [PATCH 07/60] Add route status tests --- CLAUDE.md | 1 + .../exception/RouteStatusErrorCode.java | 2 +- .../wire/CtaDetourBustimeResponse.java | 2 +- .../java/com/cta4j/alert/AlertApiTest.java | 15 + .../alert/common/AlertQualifiersTest.java | 37 ++ .../exception/Cta4jAlertExceptionTest.java | 39 ++ .../internal/impl/AlertApiImplTest.java | 68 +++ .../routestatus/RouteStatusApiImplTest.java | 433 ++++++++++++++++++ .../routestatus/RouteStatusMapperTest.java | 30 ++ .../TrainRouteStatusMapperTest.java | 42 ++ .../Cta4jRouteStatusExceptionTest.java | 41 ++ .../exception/RouteStatusErrorCodeTest.java | 25 + .../internal/wire/CtaRoutesTest.java | 44 ++ src/test/java/com/cta4j/bus/BusApiTest.java | 2 +- .../exception/Cta4jBusExceptionTest.java | 2 +- .../internal/impl/SystemTimeApiImplTest.java | 2 +- .../common/internal/util/ApiUtilsTest.java | 2 +- .../internal/wire/CtaDetourErrorTest.java | 2 +- .../internal/wire/CtaDirectionErrorTest.java | 2 +- .../internal/wire/CtaPatternErrorTest.java | 2 +- .../pattern/internal/wire/CtaPatternTest.java | 2 +- .../bus/pattern/model/RoutePatternTest.java | 2 +- .../internal/wire/CtaPredictionErrorTest.java | 2 +- .../prediction/model/DynamicActionTest.java | 2 +- .../bus/prediction/model/FlagStopTest.java | 2 +- .../bus/prediction/model/PredictionTest.java | 2 +- .../query/StopsPredictionsQueryTest.java | 2 +- .../query/VehiclesPredictionsQueryTest.java | 2 +- .../stop/internal/wire/CtaStopErrorTest.java | 2 +- .../bus/stop/internal/wire/CtaStopTest.java | 2 +- .../com/cta4j/bus/stop/model/StopTest.java | 2 +- .../internal/wire/CtaVehicleErrorTest.java | 2 +- .../bus/vehicle/model/TransitModeTest.java | 2 +- .../com/cta4j/common/geo/CoordinatesTest.java | 2 +- .../java/com/cta4j/train/TrainApiTest.java | 2 +- .../exception/ArrivalsErrorCodeTest.java | 2 +- .../exception/Cta4jArrivalsExceptionTest.java | 2 +- .../arrival/query/MapArrivalQueryTest.java | 2 +- .../arrival/query/StopArrivalQueryTest.java | 2 +- .../exception/Cta4jTrainExceptionTest.java | 2 +- .../common/model/TrainDirectionTest.java | 2 +- .../train/common/model/TrainLineTest.java | 2 +- .../exception/Cta4jFollowExceptionTest.java | 2 +- .../follow/exception/FollowErrorCodeTest.java | 2 +- .../Cta4jLocationsExceptionTest.java | 2 +- .../exception/LocationsErrorCodeTest.java | 2 +- .../station/model/CardinalDirectionTest.java | 2 +- .../alert/routestatus/bad_error_code.json | 6 + .../routestatus/blank_error_message.json | 7 + .../alert/routestatus/bus_success.json | 14 + .../routestatus/distinct_error_codes.json | 6 + .../routestatus/empty_route_info_array.json | 8 + .../routestatus/error_code_empty_array.json | 6 + .../error_message_empty_array.json | 7 + .../alert/routestatus/error_no_message.json | 6 + .../error_null_message_element.json | 7 + .../alert/routestatus/invalid_type_error.json | 7 + .../alert/routestatus/list_success.json | 36 ++ .../alert/routestatus/no_data_no_error.json | 5 + .../alert/routestatus/no_results.json | 7 + .../alert/routestatus/rail_success.json | 27 ++ 61 files changed, 960 insertions(+), 36 deletions(-) create mode 100644 src/test/java/com/cta4j/alert/AlertApiTest.java create mode 100644 src/test/java/com/cta4j/alert/common/AlertQualifiersTest.java create mode 100644 src/test/java/com/cta4j/alert/common/exception/Cta4jAlertExceptionTest.java create mode 100644 src/test/java/com/cta4j/alert/common/internal/impl/AlertApiImplTest.java create mode 100644 src/test/java/com/cta4j/alert/routestatus/RouteStatusApiImplTest.java create mode 100644 src/test/java/com/cta4j/alert/routestatus/RouteStatusMapperTest.java create mode 100644 src/test/java/com/cta4j/alert/routestatus/TrainRouteStatusMapperTest.java create mode 100644 src/test/java/com/cta4j/alert/routestatus/exception/Cta4jRouteStatusExceptionTest.java create mode 100644 src/test/java/com/cta4j/alert/routestatus/exception/RouteStatusErrorCodeTest.java create mode 100644 src/test/java/com/cta4j/alert/routestatus/internal/wire/CtaRoutesTest.java create mode 100644 src/test/resources/alert/routestatus/bad_error_code.json create mode 100644 src/test/resources/alert/routestatus/blank_error_message.json create mode 100644 src/test/resources/alert/routestatus/bus_success.json create mode 100644 src/test/resources/alert/routestatus/distinct_error_codes.json create mode 100644 src/test/resources/alert/routestatus/empty_route_info_array.json create mode 100644 src/test/resources/alert/routestatus/error_code_empty_array.json create mode 100644 src/test/resources/alert/routestatus/error_message_empty_array.json create mode 100644 src/test/resources/alert/routestatus/error_no_message.json create mode 100644 src/test/resources/alert/routestatus/error_null_message_element.json create mode 100644 src/test/resources/alert/routestatus/invalid_type_error.json create mode 100644 src/test/resources/alert/routestatus/list_success.json create mode 100644 src/test/resources/alert/routestatus/no_data_no_error.json create mode 100644 src/test/resources/alert/routestatus/no_results.json create mode 100644 src/test/resources/alert/routestatus/rail_success.json diff --git a/CLAUDE.md b/CLAUDE.md index 0afa9bae..5f7bb558 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -99,3 +99,4 @@ MapStruct mappers live in `internal/mapper/`. They are interfaces annotated with - No comments unless the why is non-obvious. - No `Optional` for fields or parameters. Use method overloading or `@Nullable` fields instead. - Prefer `List.of()` for empty returns; use `List.copyOf()` for defensive copies. +- All files must end with a trailing newline. diff --git a/src/main/java/com/cta4j/alert/routestatus/exception/RouteStatusErrorCode.java b/src/main/java/com/cta4j/alert/routestatus/exception/RouteStatusErrorCode.java index 080fba75..b55b6d5d 100644 --- a/src/main/java/com/cta4j/alert/routestatus/exception/RouteStatusErrorCode.java +++ b/src/main/java/com/cta4j/alert/routestatus/exception/RouteStatusErrorCode.java @@ -105,4 +105,4 @@ public static RouteStatusErrorCode fromCode(int code) { default -> UNKNOWN; }; } -} \ No newline at end of file +} diff --git a/src/main/java/com/cta4j/bus/detour/internal/wire/CtaDetourBustimeResponse.java b/src/main/java/com/cta4j/bus/detour/internal/wire/CtaDetourBustimeResponse.java index 68dd269f..7b7568ad 100644 --- a/src/main/java/com/cta4j/bus/detour/internal/wire/CtaDetourBustimeResponse.java +++ b/src/main/java/com/cta4j/bus/detour/internal/wire/CtaDetourBustimeResponse.java @@ -23,4 +23,4 @@ public record CtaDetourBustimeResponse( dtrs = List.copyOf(dtrs); } } -} \ No newline at end of file +} diff --git a/src/test/java/com/cta4j/alert/AlertApiTest.java b/src/test/java/com/cta4j/alert/AlertApiTest.java new file mode 100644 index 00000000..b0ba8f9c --- /dev/null +++ b/src/test/java/com/cta4j/alert/AlertApiTest.java @@ -0,0 +1,15 @@ +package com.cta4j.alert; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.*; + +class AlertApiTest { + @Test + void builder_returnsWorkingInstance() { + AlertApi api = AlertApi.builder().build(); + + assertThat(api).isNotNull(); + assertThat(api.routeStatus()).isNotNull(); + } +} diff --git a/src/test/java/com/cta4j/alert/common/AlertQualifiersTest.java b/src/test/java/com/cta4j/alert/common/AlertQualifiersTest.java new file mode 100644 index 00000000..1c2e6a47 --- /dev/null +++ b/src/test/java/com/cta4j/alert/common/AlertQualifiersTest.java @@ -0,0 +1,37 @@ +package com.cta4j.alert.common; + +import com.cta4j.alert.common.internal.mapper.Qualifiers; +import com.cta4j.common.train.TrainLine; +import org.junit.jupiter.api.Test; + +import java.net.URI; + +import static org.assertj.core.api.Assertions.*; + +class AlertQualifiersTest { + @Test + void mapUri_returnsUri_whenValueIsValid() { + URI result = Qualifiers.mapUri("http://www.transitchicago.com/redline/"); + + assertThat(result).hasToString("http://www.transitchicago.com/redline/"); + } + + @Test + void mapUri_throwsIllegalArgumentException_whenValueIsInvalid() { + assertThatIllegalArgumentException().isThrownBy(() -> Qualifiers.mapUri("not a uri")) + .withMessageContaining("Failed to parse URI") + .withCauseInstanceOf(java.net.URISyntaxException.class); + } + + @Test + void mapTrainLine_returnsTrainLine_whenCodeIsValid() { + assertThat(Qualifiers.mapTrainLine("Red")).isEqualTo(TrainLine.RED); + } + + @Test + void mapTrainLine_throwsIllegalArgumentException_whenCodeIsInvalid() { + assertThatIllegalArgumentException().isThrownBy(() -> Qualifiers.mapTrainLine("22")) + .withMessage("Failed to parse train line code: 22") + .withCauseInstanceOf(IllegalArgumentException.class); + } +} diff --git a/src/test/java/com/cta4j/alert/common/exception/Cta4jAlertExceptionTest.java b/src/test/java/com/cta4j/alert/common/exception/Cta4jAlertExceptionTest.java new file mode 100644 index 00000000..a04d9bae --- /dev/null +++ b/src/test/java/com/cta4j/alert/common/exception/Cta4jAlertExceptionTest.java @@ -0,0 +1,39 @@ +package com.cta4j.alert.common.exception; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.*; + +class Cta4jAlertExceptionTest { + @Test + void constructor_setsMessageAndEndpoint() { + Cta4jAlertException exception = new Cta4jAlertException("Failed to parse response", "/routes.aspx"); + + assertThat(exception.getMessage()).isEqualTo("Failed to parse response"); + assertThat(exception.getEndpoint()).isEqualTo("/routes.aspx"); + assertThat(exception.getCause()).isNull(); + assertThat(exception.getRawErrorCode()).isNull(); + } + + @Test + void constructor_setsMessageEndpointAndCause() { + Throwable cause = new RuntimeException("root cause"); + + Cta4jAlertException exception = new Cta4jAlertException("Failed to parse response", "/routes.aspx", cause); + + assertThat(exception.getMessage()).isEqualTo("Failed to parse response"); + assertThat(exception.getEndpoint()).isEqualTo("/routes.aspx"); + assertThat(exception.getCause()).isSameAs(cause); + assertThat(exception.getRawErrorCode()).isNull(); + } + + @Test + void constructor_setsMessageEndpointAndRawErrorCode() { + Cta4jAlertException exception = new Cta4jAlertException("Invalid parameter", "/routes.aspx", 500); + + assertThat(exception.getMessage()).isEqualTo("Invalid parameter"); + assertThat(exception.getEndpoint()).isEqualTo("/routes.aspx"); + assertThat(exception.getCause()).isNull(); + assertThat(exception.getRawErrorCode()).isEqualTo(500); + } +} diff --git a/src/test/java/com/cta4j/alert/common/internal/impl/AlertApiImplTest.java b/src/test/java/com/cta4j/alert/common/internal/impl/AlertApiImplTest.java new file mode 100644 index 00000000..6ceb3b9f --- /dev/null +++ b/src/test/java/com/cta4j/alert/common/internal/impl/AlertApiImplTest.java @@ -0,0 +1,68 @@ +package com.cta4j.alert.common.internal.impl; + +import com.cta4j.alert.AlertApi; +import com.cta4j.alert.common.internal.config.AlertApiConfig; +import com.cta4j.alert.routestatus.RouteStatusApi; +import com.github.tomakehurst.wiremock.WireMockServer; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; +import static org.assertj.core.api.Assertions.*; + +class AlertApiImplTest { + private WireMockServer server; + private AlertApiImpl api; + + @BeforeEach + void setUp() { + this.server = new WireMockServer(wireMockConfig().dynamicPort()); + this.server.start(); + AlertApiConfig config = new AlertApiConfig("http", "localhost", this.server.port()); + this.api = new AlertApiImpl(config); + } + + @AfterEach + void tearDown() { + this.server.stop(); + } + + @Test + void constructor_throwsNullPointerException_whenConfigIsNull() { + assertThatNullPointerException().isThrownBy(() -> new AlertApiImpl(null)); + } + + @Test + void routeStatus_returnsNonNull() { + RouteStatusApi result = this.api.routeStatus(); + + assertThat(result).isNotNull(); + } + + @Test + void builderImpl_host_throwsNullPointerException_whenHostIsNull() { + AlertApiImpl.BuilderImpl builder = new AlertApiImpl.BuilderImpl(); + + assertThatNullPointerException().isThrownBy(() -> builder.host(null)); + } + + @Test + void builderImpl_build_returnsInstance_withDefaultHost() { + AlertApiImpl.BuilderImpl builder = new AlertApiImpl.BuilderImpl(); + + AlertApi result = builder.build(); + + assertThat(result).isNotNull(); + } + + @Test + void builderImpl_build_returnsInstance_withCustomHost() { + AlertApiImpl.BuilderImpl builder = new AlertApiImpl.BuilderImpl(); + builder.host("example.com"); + + AlertApi result = builder.build(); + + assertThat(result).isNotNull(); + } +} diff --git a/src/test/java/com/cta4j/alert/routestatus/RouteStatusApiImplTest.java b/src/test/java/com/cta4j/alert/routestatus/RouteStatusApiImplTest.java new file mode 100644 index 00000000..19b91540 --- /dev/null +++ b/src/test/java/com/cta4j/alert/routestatus/RouteStatusApiImplTest.java @@ -0,0 +1,433 @@ +package com.cta4j.alert.routestatus; + +import com.cta4j.TestFixtures; +import com.cta4j.alert.common.internal.config.AlertApiConfig; +import com.cta4j.alert.common.internal.util.AlertApiConstants; +import com.cta4j.alert.routestatus.exception.Cta4jRouteStatusException; +import com.cta4j.alert.routestatus.exception.RouteStatusErrorCode; +import com.cta4j.alert.routestatus.internal.impl.RouteStatusApiImpl; +import com.cta4j.alert.routestatus.model.RouteStatus; +import com.cta4j.alert.routestatus.model.ServiceType; +import com.cta4j.alert.routestatus.model.TrainRouteStatus; +import com.cta4j.common.train.TrainLine; +import com.github.tomakehurst.wiremock.WireMockServer; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import tools.jackson.core.JacksonException; + +import java.util.List; + +import static com.github.tomakehurst.wiremock.client.WireMock.*; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; +import static org.assertj.core.api.Assertions.*; + +class RouteStatusApiImplTest { + private WireMockServer server; + private RouteStatusApiImpl api; + + @BeforeEach + void setUp() { + this.server = new WireMockServer(wireMockConfig().dynamicPort()); + this.server.start(); + AlertApiConfig config = new AlertApiConfig("http", "localhost", this.server.port()); + this.api = new RouteStatusApiImpl(config); + } + + @AfterEach + void tearDown() { + this.server.stop(); + } + + @Test + void list_returnsRouteStatuses_whenResponseContainsData() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/routes.aspx")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("alert/routestatus/list_success.json")))); + + List statuses = this.api.list(); + + assertThat(statuses).hasSize(3); + RouteStatus redLine = statuses.getFirst(); + assertThat(redLine.route()).isEqualTo("Red Line"); + assertThat(redLine.color()).isEqualTo("c60c30"); + assertThat(redLine.textColor()).isEqualTo("ffffff"); + assertThat(redLine.serviceId()).isEqualTo("Red"); + assertThat(redLine.url()).hasToString("http://www.transitchicago.com/redline/"); + assertThat(redLine.status()).isEqualTo("Normal Service"); + assertThat(redLine.statusColor()).isEqualTo("404040"); + } + + @Test + void list_returnsEmpty_whenErrorCodeIsExplicitlyEmptyArray() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/routes.aspx")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("alert/routestatus/error_code_empty_array.json")))); + + List statuses = this.api.list(); + + assertThat(statuses).isEmpty(); + } + + @Test + void list_returnsEmpty_whenNoRouteInfoAndNoErrorCode() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/routes.aspx")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("alert/routestatus/no_data_no_error.json")))); + + List statuses = this.api.list(); + + assertThat(statuses).isEmpty(); + } + + @Test + void list_returnsEmpty_whenRouteInfoIsExplicitlyEmptyArray() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/routes.aspx")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("alert/routestatus/empty_route_info_array.json")))); + + List statuses = this.api.list(); + + assertThat(statuses).isEmpty(); + } + + @Test + void list_returnsEmpty_whenErrorCodeIsNoResults() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/routes.aspx")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("alert/routestatus/no_results.json")))); + + List statuses = this.api.list(); + + assertThat(statuses).isEmpty(); + } + + @Test + void list_returnsEmpty_whenErrorCodesAreDistinct_usingFirstValue() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/routes.aspx")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("alert/routestatus/distinct_error_codes.json")))); + + List statuses = this.api.list(); + + assertThat(statuses).isEmpty(); + } + + @Test + void list_throwsCta4jRouteStatusException_whenResponseContainsFatalError() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/routes.aspx")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("alert/routestatus/invalid_type_error.json")))); + + assertThatThrownBy(() -> this.api.list()) + .isInstanceOf(Cta4jRouteStatusException.class) + .hasMessage("Invalid option for parameter 'type': Valid options are 'bus', 'rail', 'station' or 'systemwide'") + .satisfies(e -> assertThat(((Cta4jRouteStatusException) e).getErrorCode()) + .isEqualTo(RouteStatusErrorCode.INVALID_TYPE)) + .satisfies(e -> assertThat(((Cta4jRouteStatusException) e).getRawErrorCode()).isEqualTo(101)) + .satisfies(e -> assertThat(((Cta4jRouteStatusException) e).getEndpoint()) + .isEqualTo(AlertApiConstants.ROUTE_STATUS_ENDPOINT)); + } + + @Test + void list_throwsCta4jRouteStatusException_withDefaultMessage_whenErrorMessageIsExplicitlyEmptyArray() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/routes.aspx")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("alert/routestatus/error_message_empty_array.json")))); + + assertThatThrownBy(() -> this.api.list()) + .isInstanceOf(Cta4jRouteStatusException.class) + .hasMessage("An unknown error occurred.") + .satisfies(e -> assertThat(((Cta4jRouteStatusException) e).getErrorCode()) + .isEqualTo(RouteStatusErrorCode.INVALID_TYPE)); + } + + @Test + void list_throwsCta4jRouteStatusException_withDefaultMessage_whenErrorMessageIsBlank() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/routes.aspx")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("alert/routestatus/blank_error_message.json")))); + + assertThatThrownBy(() -> this.api.list()) + .isInstanceOf(Cta4jRouteStatusException.class) + .hasMessage("An unknown error occurred.") + .satisfies(e -> assertThat(((Cta4jRouteStatusException) e).getErrorCode()) + .isEqualTo(RouteStatusErrorCode.SERVER_ERROR)); + } + + @Test + void list_throwsCta4jRouteStatusException_withDefaultMessage_whenErrorMessageElementIsNull() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/routes.aspx")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("alert/routestatus/error_null_message_element.json")))); + + assertThatThrownBy(() -> this.api.list()) + .isInstanceOf(Cta4jRouteStatusException.class) + .hasMessage("An unknown error occurred.") + .satisfies(e -> assertThat(((Cta4jRouteStatusException) e).getErrorCode()) + .isEqualTo(RouteStatusErrorCode.INVALID_TYPE)); + } + + @Test + void list_throwsCta4jRouteStatusException_withDefaultMessage_whenErrorMessageIsAbsent() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/routes.aspx")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("alert/routestatus/error_no_message.json")))); + + assertThatThrownBy(() -> this.api.list()) + .isInstanceOf(Cta4jRouteStatusException.class) + .hasMessage("An unknown error occurred.") + .satisfies(e -> assertThat(((Cta4jRouteStatusException) e).getErrorCode()) + .isEqualTo(RouteStatusErrorCode.INVALID_TYPE)); + } + + @Test + void list_throwsCta4jRouteStatusException_whenErrorCodeIsNotNumeric() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/routes.aspx")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("alert/routestatus/bad_error_code.json")))); + + assertThatThrownBy(() -> this.api.list()) + .isInstanceOf(Cta4jRouteStatusException.class) + .hasMessage("Failed to parse error code") + .satisfies(e -> assertThat(e.getCause()).isInstanceOf(NumberFormatException.class)); + } + + @Test + void list_throwsCta4jRouteStatusException_whenResponseIsNotJson() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/routes.aspx")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody("not-json"))); + + assertThatThrownBy(() -> this.api.list()) + .isInstanceOf(Cta4jRouteStatusException.class) + .hasMessage("Failed to parse response") + .satisfies(e -> assertThat(((Cta4jRouteStatusException) e).getEndpoint()) + .isEqualTo(AlertApiConstants.ROUTE_STATUS_ENDPOINT)) + .satisfies(e -> assertThat(e.getCause()).isInstanceOf(JacksonException.class)); + } + + @Test + void list_throwsCta4jRouteStatusException_whenServerReturnsErrorStatus() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/routes.aspx")) + .willReturn(aResponse() + .withStatus(500))); + + assertThatThrownBy(() -> this.api.list()) + .isInstanceOf(Cta4jRouteStatusException.class) + .hasMessageContaining("500") + .satisfies(e -> assertThat(e.getCause()).isNotNull()); + } + + @Test + void findByTypes_returnsEmpty_whenInputIsEmpty() { + List statuses = this.api.findByTypes(List.of()); + + assertThat(statuses).isEmpty(); + this.server.verify(0, anyRequestedFor(anyUrl())); + } + + @Test + void findByTypes_sendsTypeParameter_asCommaJoinedLowercase() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/routes.aspx")) + .withQueryParam("type", equalTo("bus,rail")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("alert/routestatus/list_success.json")))); + + List statuses = this.api.findByTypes(List.of(ServiceType.BUS, ServiceType.RAIL)); + + assertThat(statuses).hasSize(3); + } + + @Test + void findByType_delegatesToFindByTypes() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/routes.aspx")) + .withQueryParam("type", equalTo("rail")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("alert/routestatus/rail_success.json")))); + + List statuses = this.api.findByType(ServiceType.RAIL); + + assertThat(statuses).hasSize(2); + } + + @Test + void findByBusRouteIds_returnsEmpty_whenInputIsEmpty() { + List statuses = this.api.findByBusRouteIds(List.of()); + + assertThat(statuses).isEmpty(); + this.server.verify(0, anyRequestedFor(anyUrl())); + } + + @Test + void findByBusRouteIds_sendsRouteidParameter_asCommaJoined() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/routes.aspx")) + .withQueryParam("routeid", equalTo("22,53")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("alert/routestatus/bus_success.json")))); + + List statuses = this.api.findByBusRouteIds(List.of("22", "53")); + + assertThat(statuses).hasSize(1); + } + + @Test + void findByBusRouteIds_returnsRouteStatuses_whenResponseOmitsErrorEnvelope() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/routes.aspx")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("alert/routestatus/bus_success.json")))); + + List statuses = this.api.findByBusRouteIds(List.of("22")); + + assertThat(statuses).hasSize(1); + RouteStatus status = statuses.getFirst(); + assertThat(status.serviceId()).isEqualTo("22"); + assertThat(status.status()).isEqualTo("Bus Stop Note"); + } + + @Test + void findByBusRouteIds_throwsIllegalArgumentException_whenRouteIdIsTrainLine() { + assertThatIllegalArgumentException() + .isThrownBy(() -> this.api.findByBusRouteIds(List.of("Red"))) + .withMessageContaining("Red is a train line, not a bus route"); + + this.server.verify(0, anyRequestedFor(anyUrl())); + } + + @Test + void findByBusRouteIds_throwsIllegalArgumentException_whenRouteIdIsTrainLine_caseInsensitive() { + assertThatIllegalArgumentException() + .isThrownBy(() -> this.api.findByBusRouteIds(List.of("red"))); + + this.server.verify(0, anyRequestedFor(anyUrl())); + } + + @Test + void findByBusRouteId_delegatesToFindByBusRouteIds() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/routes.aspx")) + .withQueryParam("routeid", equalTo("22")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("alert/routestatus/bus_success.json")))); + + List statuses = this.api.findByBusRouteId("22"); + + assertThat(statuses).hasSize(1); + } + + @Test + void findByLines_returnsEmpty_whenInputIsEmpty() { + List statuses = this.api.findByLines(List.of()); + + assertThat(statuses).isEmpty(); + this.server.verify(0, anyRequestedFor(anyUrl())); + } + + @Test + void findByLines_sendsRouteidParameter_asCommaJoinedCodes() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/routes.aspx")) + .withQueryParam("routeid", equalTo("Red,Blue")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("alert/routestatus/rail_success.json")))); + + List statuses = this.api.findByLines(List.of(TrainLine.RED, TrainLine.BLUE)); + + assertThat(statuses).hasSize(2); + } + + @Test + void findByLines_returnsTrainRouteStatuses_whenResponseContainsData() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/routes.aspx")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("alert/routestatus/rail_success.json")))); + + List statuses = this.api.findByLines(List.of(TrainLine.RED, TrainLine.BLUE)); + + assertThat(statuses).hasSize(2); + TrainRouteStatus redLine = statuses.getFirst(); + assertThat(redLine.line()).isEqualTo(TrainLine.RED); + assertThat(redLine.route()).isEqualTo("Red Line"); + assertThat(redLine.status()).isEqualTo("Normal Service"); + } + + @Test + void findByLine_delegatesToFindByLines() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/routes.aspx")) + .withQueryParam("routeid", equalTo("Red")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("alert/routestatus/rail_success.json")))); + + List statuses = this.api.findByLine(TrainLine.RED); + + assertThat(statuses).hasSize(2); + } + + @Test + void findByStationId_sendsStationidParameter() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/routes.aspx")) + .withQueryParam("stationid", equalTo("40380")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("alert/routestatus/rail_success.json")))); + + List statuses = this.api.findByStationId("40380"); + + assertThat(statuses).hasSize(2); + } + + @Test + void findByStationId_returnsRouteStatuses_whenResponseContainsData() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/routes.aspx")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("alert/routestatus/bus_success.json")))); + + List statuses = this.api.findByStationId("40380"); + + assertThat(statuses).hasSize(1); + assertThat(statuses.getFirst().serviceId()).isEqualTo("22"); + } +} diff --git a/src/test/java/com/cta4j/alert/routestatus/RouteStatusMapperTest.java b/src/test/java/com/cta4j/alert/routestatus/RouteStatusMapperTest.java new file mode 100644 index 00000000..38685de1 --- /dev/null +++ b/src/test/java/com/cta4j/alert/routestatus/RouteStatusMapperTest.java @@ -0,0 +1,30 @@ +package com.cta4j.alert.routestatus; + +import com.cta4j.alert.routestatus.internal.mapper.RouteStatusMapper; +import com.cta4j.alert.routestatus.internal.wire.CtaRouteInfo; +import com.cta4j.alert.routestatus.internal.wire.CtaRouteInfoUrl; +import com.cta4j.alert.routestatus.model.RouteStatus; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.*; + +class RouteStatusMapperTest { + @Test + void toDomain_mapsAllFields() { + CtaRouteInfo wire = new CtaRouteInfo( + "Clark", "565a5c", "ffffff", "22", + new CtaRouteInfoUrl("http://www.transitchicago.com/bus/22/"), + "Bus Stop Note", "000000" + ); + + RouteStatus status = RouteStatusMapper.INSTANCE.toDomain(wire); + + assertThat(status.route()).isEqualTo("Clark"); + assertThat(status.color()).isEqualTo("565a5c"); + assertThat(status.textColor()).isEqualTo("ffffff"); + assertThat(status.serviceId()).isEqualTo("22"); + assertThat(status.url()).hasToString("http://www.transitchicago.com/bus/22/"); + assertThat(status.status()).isEqualTo("Bus Stop Note"); + assertThat(status.statusColor()).isEqualTo("000000"); + } +} diff --git a/src/test/java/com/cta4j/alert/routestatus/TrainRouteStatusMapperTest.java b/src/test/java/com/cta4j/alert/routestatus/TrainRouteStatusMapperTest.java new file mode 100644 index 00000000..028dc63b --- /dev/null +++ b/src/test/java/com/cta4j/alert/routestatus/TrainRouteStatusMapperTest.java @@ -0,0 +1,42 @@ +package com.cta4j.alert.routestatus; + +import com.cta4j.alert.routestatus.internal.mapper.TrainRouteStatusMapper; +import com.cta4j.alert.routestatus.internal.wire.CtaRouteInfo; +import com.cta4j.alert.routestatus.internal.wire.CtaRouteInfoUrl; +import com.cta4j.alert.routestatus.model.TrainRouteStatus; +import com.cta4j.common.train.TrainLine; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.*; + +class TrainRouteStatusMapperTest { + @Test + void toDomain_mapsAllFields() { + CtaRouteInfo wire = new CtaRouteInfo( + "Red Line", "c60c30", "ffffff", "Red", + new CtaRouteInfoUrl("http://www.transitchicago.com/redline/"), + "Normal Service", "404040" + ); + + TrainRouteStatus status = TrainRouteStatusMapper.INSTANCE.toDomain(wire); + + assertThat(status.route()).isEqualTo("Red Line"); + assertThat(status.color()).isEqualTo("c60c30"); + assertThat(status.textColor()).isEqualTo("ffffff"); + assertThat(status.line()).isEqualTo(TrainLine.RED); + assertThat(status.url()).hasToString("http://www.transitchicago.com/redline/"); + assertThat(status.status()).isEqualTo("Normal Service"); + assertThat(status.statusColor()).isEqualTo("404040"); + } + + @Test + void toDomain_throwsIllegalArgumentException_whenServiceIdIsNotATrainLine() { + CtaRouteInfo wire = new CtaRouteInfo( + "Clark", "565a5c", "ffffff", "22", + new CtaRouteInfoUrl("http://www.transitchicago.com/bus/22/"), + "Bus Stop Note", "000000" + ); + + assertThatIllegalArgumentException().isThrownBy(() -> TrainRouteStatusMapper.INSTANCE.toDomain(wire)); + } +} diff --git a/src/test/java/com/cta4j/alert/routestatus/exception/Cta4jRouteStatusExceptionTest.java b/src/test/java/com/cta4j/alert/routestatus/exception/Cta4jRouteStatusExceptionTest.java new file mode 100644 index 00000000..134698e5 --- /dev/null +++ b/src/test/java/com/cta4j/alert/routestatus/exception/Cta4jRouteStatusExceptionTest.java @@ -0,0 +1,41 @@ +package com.cta4j.alert.routestatus.exception; + +import com.cta4j.alert.common.internal.util.AlertApiConstants; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.*; + +class Cta4jRouteStatusExceptionTest { + @Test + void constructor_setsMessageEndpointAndCause_andLeavesErrorCodeNull() { + Throwable cause = new RuntimeException("root cause"); + + Cta4jRouteStatusException exception = new Cta4jRouteStatusException("Failed to parse response", cause); + + assertThat(exception.getMessage()).isEqualTo("Failed to parse response"); + assertThat(exception.getEndpoint()).isEqualTo(AlertApiConstants.ROUTE_STATUS_ENDPOINT); + assertThat(exception.getCause()).isSameAs(cause); + assertThat(exception.getRawErrorCode()).isNull(); + assertThat(exception.getErrorCode()).isNull(); + } + + @Test + void constructor_setsMessageEndpointAndErrorCode() { + Cta4jRouteStatusException exception = + new Cta4jRouteStatusException("Invalid option for parameter 'type'", 101); + + assertThat(exception.getMessage()).isEqualTo("Invalid option for parameter 'type'"); + assertThat(exception.getEndpoint()).isEqualTo(AlertApiConstants.ROUTE_STATUS_ENDPOINT); + assertThat(exception.getCause()).isNull(); + assertThat(exception.getRawErrorCode()).isEqualTo(101); + assertThat(exception.getErrorCode()).isEqualTo(RouteStatusErrorCode.INVALID_TYPE); + } + + @Test + void constructor_setsUnknownErrorCode_whenRawErrorCodeIsUnrecognized() { + Cta4jRouteStatusException exception = new Cta4jRouteStatusException("Something odd happened", 999); + + assertThat(exception.getRawErrorCode()).isEqualTo(999); + assertThat(exception.getErrorCode()).isEqualTo(RouteStatusErrorCode.UNKNOWN); + } +} diff --git a/src/test/java/com/cta4j/alert/routestatus/exception/RouteStatusErrorCodeTest.java b/src/test/java/com/cta4j/alert/routestatus/exception/RouteStatusErrorCodeTest.java new file mode 100644 index 00000000..636a437e --- /dev/null +++ b/src/test/java/com/cta4j/alert/routestatus/exception/RouteStatusErrorCodeTest.java @@ -0,0 +1,25 @@ +package com.cta4j.alert.routestatus.exception; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.*; + +class RouteStatusErrorCodeTest { + @Test + void fromCode_returnsCorrectValue_forEveryDefinedCode() { + for (RouteStatusErrorCode code : RouteStatusErrorCode.values()) { + assertThat(RouteStatusErrorCode.fromCode(code.getCode())).isEqualTo(code); + } + } + + @Test + void fromCode_returnsUnknown_whenCodeIsUnrecognized() { + assertThat(RouteStatusErrorCode.fromCode(12345)).isEqualTo(RouteStatusErrorCode.UNKNOWN); + } + + @Test + void getCode_returnsCode() { + assertThat(RouteStatusErrorCode.NO_RESULTS.getCode()).isEqualTo(50); + assertThat(RouteStatusErrorCode.UNKNOWN.getCode()).isEqualTo(-1); + } +} diff --git a/src/test/java/com/cta4j/alert/routestatus/internal/wire/CtaRoutesTest.java b/src/test/java/com/cta4j/alert/routestatus/internal/wire/CtaRoutesTest.java new file mode 100644 index 00000000..94aa780d --- /dev/null +++ b/src/test/java/com/cta4j/alert/routestatus/internal/wire/CtaRoutesTest.java @@ -0,0 +1,44 @@ +package com.cta4j.alert.routestatus.internal.wire; + +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static org.assertj.core.api.Assertions.*; + +class CtaRoutesTest { + @Test + void constructor_copiesErrorCode_whenNonNull() { + List errorCode = new ArrayList<>(List.of("0", "0")); + + CtaRoutes routes = new CtaRoutes("2026-07-17T14:06:58", errorCode, null, null); + errorCode.add("50"); + + assertThat(routes.errorCode()).containsExactly("0", "0"); + } + + @Test + void constructor_copiesErrorMessage_whenNonNull_andAllowsNullElements() { + List errorMessage = new ArrayList<>(Arrays.asList(null, null)); + + CtaRoutes routes = new CtaRoutes("2026-07-17T14:06:58", List.of("0", "0"), errorMessage, null); + errorMessage.add("late addition"); + + assertThat(routes.errorMessage()).containsExactly(null, null); + } + + @Test + void constructor_allowsNullErrorCodeAndErrorMessage() { + CtaRoutes routes = new CtaRoutes("2026-07-17T14:06:58", null, null, null); + + assertThat(routes.errorCode()).isNull(); + assertThat(routes.errorMessage()).isNull(); + } + + @Test + void constructor_throwsNullPointerException_whenTimestampIsNull() { + assertThatNullPointerException().isThrownBy(() -> new CtaRoutes(null, null, null, null)); + } +} diff --git a/src/test/java/com/cta4j/bus/BusApiTest.java b/src/test/java/com/cta4j/bus/BusApiTest.java index 267430a0..a5f78985 100644 --- a/src/test/java/com/cta4j/bus/BusApiTest.java +++ b/src/test/java/com/cta4j/bus/BusApiTest.java @@ -17,4 +17,4 @@ void builder_returnsWorkingInstance() { assertThat(api).isNotNull(); assertThat(api.vehicles()).isNotNull(); } -} \ No newline at end of file +} diff --git a/src/test/java/com/cta4j/bus/common/exception/Cta4jBusExceptionTest.java b/src/test/java/com/cta4j/bus/common/exception/Cta4jBusExceptionTest.java index acc2eec7..58355419 100644 --- a/src/test/java/com/cta4j/bus/common/exception/Cta4jBusExceptionTest.java +++ b/src/test/java/com/cta4j/bus/common/exception/Cta4jBusExceptionTest.java @@ -61,4 +61,4 @@ void constructor_setsMessageAndEndpoint_withNoErrorsOrCause() { assertThat(exception.getEndpoint()).isEqualTo("/bustime/api/v3/getstops"); assertThat(exception.getCause()).isNull(); } -} \ No newline at end of file +} diff --git a/src/test/java/com/cta4j/bus/common/internal/impl/SystemTimeApiImplTest.java b/src/test/java/com/cta4j/bus/common/internal/impl/SystemTimeApiImplTest.java index de47ddd4..2d4341d0 100644 --- a/src/test/java/com/cta4j/bus/common/internal/impl/SystemTimeApiImplTest.java +++ b/src/test/java/com/cta4j/bus/common/internal/impl/SystemTimeApiImplTest.java @@ -105,4 +105,4 @@ void systemTime_throwsCta4jBusException_whenServerReturnsErrorStatus() { .isEqualTo(BusApiConstants.SYSTEM_TIME_ENDPOINT)) .satisfies(e -> assertThat(e.getCause()).isNotNull()); } -} \ No newline at end of file +} diff --git a/src/test/java/com/cta4j/bus/common/internal/util/ApiUtilsTest.java b/src/test/java/com/cta4j/bus/common/internal/util/ApiUtilsTest.java index 78728f13..d9d47513 100644 --- a/src/test/java/com/cta4j/bus/common/internal/util/ApiUtilsTest.java +++ b/src/test/java/com/cta4j/bus/common/internal/util/ApiUtilsTest.java @@ -68,4 +68,4 @@ void checkErrors_throwsCta4jBusException_whenAnyErrorIsNotResourceSpecific() { void checkErrors_throwsNullPointerException_whenEndpointIsNull() { assertThatNullPointerException().isThrownBy(() -> ApiUtils.checkErrors(List.of(), null)); } -} \ No newline at end of file +} diff --git a/src/test/java/com/cta4j/bus/detour/internal/wire/CtaDetourErrorTest.java b/src/test/java/com/cta4j/bus/detour/internal/wire/CtaDetourErrorTest.java index 0902ac6f..24856478 100644 --- a/src/test/java/com/cta4j/bus/detour/internal/wire/CtaDetourErrorTest.java +++ b/src/test/java/com/cta4j/bus/detour/internal/wire/CtaDetourErrorTest.java @@ -18,4 +18,4 @@ void notFound_returnsFalse_whenRtAbsent() { assertThat(error.notFound()).isFalse(); } -} \ No newline at end of file +} diff --git a/src/test/java/com/cta4j/bus/direction/internal/wire/CtaDirectionErrorTest.java b/src/test/java/com/cta4j/bus/direction/internal/wire/CtaDirectionErrorTest.java index 8130b4b8..0d77b484 100644 --- a/src/test/java/com/cta4j/bus/direction/internal/wire/CtaDirectionErrorTest.java +++ b/src/test/java/com/cta4j/bus/direction/internal/wire/CtaDirectionErrorTest.java @@ -18,4 +18,4 @@ void notFound_returnsFalse_whenRtAbsent() { assertThat(error.notFound()).isFalse(); } -} \ No newline at end of file +} diff --git a/src/test/java/com/cta4j/bus/pattern/internal/wire/CtaPatternErrorTest.java b/src/test/java/com/cta4j/bus/pattern/internal/wire/CtaPatternErrorTest.java index 2fef2ce6..3fa9094d 100644 --- a/src/test/java/com/cta4j/bus/pattern/internal/wire/CtaPatternErrorTest.java +++ b/src/test/java/com/cta4j/bus/pattern/internal/wire/CtaPatternErrorTest.java @@ -25,4 +25,4 @@ void notFound_returnsFalse_whenNeitherPidNorRtPresent() { assertThat(error.notFound()).isFalse(); } -} \ No newline at end of file +} diff --git a/src/test/java/com/cta4j/bus/pattern/internal/wire/CtaPatternTest.java b/src/test/java/com/cta4j/bus/pattern/internal/wire/CtaPatternTest.java index a47e5c34..1de9205d 100644 --- a/src/test/java/com/cta4j/bus/pattern/internal/wire/CtaPatternTest.java +++ b/src/test/java/com/cta4j/bus/pattern/internal/wire/CtaPatternTest.java @@ -18,4 +18,4 @@ void constructor_copiesDetourPoints_whenNonNull() { assertThat(pattern.dtrpt()).containsExactly(point); } -} \ No newline at end of file +} diff --git a/src/test/java/com/cta4j/bus/pattern/model/RoutePatternTest.java b/src/test/java/com/cta4j/bus/pattern/model/RoutePatternTest.java index 02687749..e23be6b4 100644 --- a/src/test/java/com/cta4j/bus/pattern/model/RoutePatternTest.java +++ b/src/test/java/com/cta4j/bus/pattern/model/RoutePatternTest.java @@ -22,4 +22,4 @@ void constructor_copiesDetourPoints_whenNonNull() { assertThat(pattern.detourPoints()).containsExactly(point); } -} \ No newline at end of file +} diff --git a/src/test/java/com/cta4j/bus/prediction/internal/wire/CtaPredictionErrorTest.java b/src/test/java/com/cta4j/bus/prediction/internal/wire/CtaPredictionErrorTest.java index 28326b30..2ed1eba7 100644 --- a/src/test/java/com/cta4j/bus/prediction/internal/wire/CtaPredictionErrorTest.java +++ b/src/test/java/com/cta4j/bus/prediction/internal/wire/CtaPredictionErrorTest.java @@ -25,4 +25,4 @@ void notFound_returnsFalse_whenNeitherStpidNorVidPresent() { assertThat(error.notFound()).isFalse(); } -} \ No newline at end of file +} diff --git a/src/test/java/com/cta4j/bus/prediction/model/DynamicActionTest.java b/src/test/java/com/cta4j/bus/prediction/model/DynamicActionTest.java index 7da01f3d..753093d6 100644 --- a/src/test/java/com/cta4j/bus/prediction/model/DynamicActionTest.java +++ b/src/test/java/com/cta4j/bus/prediction/model/DynamicActionTest.java @@ -36,4 +36,4 @@ void getCode_returnsCode() { assertThat(DynamicAction.NONE.getCode()).isEqualTo(0); assertThat(DynamicAction.CANCELLED.getCode()).isEqualTo(1); } -} \ No newline at end of file +} diff --git a/src/test/java/com/cta4j/bus/prediction/model/FlagStopTest.java b/src/test/java/com/cta4j/bus/prediction/model/FlagStopTest.java index 512f88ec..d4fadc57 100644 --- a/src/test/java/com/cta4j/bus/prediction/model/FlagStopTest.java +++ b/src/test/java/com/cta4j/bus/prediction/model/FlagStopTest.java @@ -23,4 +23,4 @@ void getCode_returnsCode() { assertThat(FlagStop.UNDEFINED.getCode()).isEqualTo(-1); assertThat(FlagStop.NORMAL.getCode()).isEqualTo(0); } -} \ No newline at end of file +} diff --git a/src/test/java/com/cta4j/bus/prediction/model/PredictionTest.java b/src/test/java/com/cta4j/bus/prediction/model/PredictionTest.java index 41d58965..ce970117 100644 --- a/src/test/java/com/cta4j/bus/prediction/model/PredictionTest.java +++ b/src/test/java/com/cta4j/bus/prediction/model/PredictionTest.java @@ -38,4 +38,4 @@ void etaMinutes_returnsZero_whenArrivalTimeIsInPast() { assertThat(eta).isZero(); } -} \ No newline at end of file +} diff --git a/src/test/java/com/cta4j/bus/prediction/query/StopsPredictionsQueryTest.java b/src/test/java/com/cta4j/bus/prediction/query/StopsPredictionsQueryTest.java index 8964ad06..87df1550 100644 --- a/src/test/java/com/cta4j/bus/prediction/query/StopsPredictionsQueryTest.java +++ b/src/test/java/com/cta4j/bus/prediction/query/StopsPredictionsQueryTest.java @@ -54,4 +54,4 @@ void constructor_throwsIllegalArgumentException_whenMaxResultsIsNotPositive() { assertThatIllegalArgumentException().isThrownBy(() -> new StopsPredictionsQuery(List.of("1001"), null, 0)); } -} \ No newline at end of file +} diff --git a/src/test/java/com/cta4j/bus/prediction/query/VehiclesPredictionsQueryTest.java b/src/test/java/com/cta4j/bus/prediction/query/VehiclesPredictionsQueryTest.java index a72f78f1..888ce761 100644 --- a/src/test/java/com/cta4j/bus/prediction/query/VehiclesPredictionsQueryTest.java +++ b/src/test/java/com/cta4j/bus/prediction/query/VehiclesPredictionsQueryTest.java @@ -51,4 +51,4 @@ void constructor_throwsIllegalArgumentException_whenMaxResultsIsNotPositive() { assertThatIllegalArgumentException().isThrownBy(() -> new VehiclesPredictionsQuery(List.of("509"), 0)); } -} \ No newline at end of file +} diff --git a/src/test/java/com/cta4j/bus/stop/internal/wire/CtaStopErrorTest.java b/src/test/java/com/cta4j/bus/stop/internal/wire/CtaStopErrorTest.java index d9b9b85f..7a30eb33 100644 --- a/src/test/java/com/cta4j/bus/stop/internal/wire/CtaStopErrorTest.java +++ b/src/test/java/com/cta4j/bus/stop/internal/wire/CtaStopErrorTest.java @@ -32,4 +32,4 @@ void notFound_returnsFalse_whenNoTypedFieldsPresent() { assertThat(error.notFound()).isFalse(); } -} \ No newline at end of file +} diff --git a/src/test/java/com/cta4j/bus/stop/internal/wire/CtaStopTest.java b/src/test/java/com/cta4j/bus/stop/internal/wire/CtaStopTest.java index 83271b8a..8122f416 100644 --- a/src/test/java/com/cta4j/bus/stop/internal/wire/CtaStopTest.java +++ b/src/test/java/com/cta4j/bus/stop/internal/wire/CtaStopTest.java @@ -27,4 +27,4 @@ void constructor_copiesDetoursRemoved_whenNonNull() { assertThat(stop.dtrrem()).containsExactly(321, 654); } -} \ No newline at end of file +} diff --git a/src/test/java/com/cta4j/bus/stop/model/StopTest.java b/src/test/java/com/cta4j/bus/stop/model/StopTest.java index ad366b0e..2b9e87d1 100644 --- a/src/test/java/com/cta4j/bus/stop/model/StopTest.java +++ b/src/test/java/com/cta4j/bus/stop/model/StopTest.java @@ -34,4 +34,4 @@ void constructor_copiesDetoursRemoved_whenNonNull() { assertThat(stop.detoursRemoved()).containsExactly(321, 654); } -} \ No newline at end of file +} diff --git a/src/test/java/com/cta4j/bus/vehicle/internal/wire/CtaVehicleErrorTest.java b/src/test/java/com/cta4j/bus/vehicle/internal/wire/CtaVehicleErrorTest.java index c096c442..64257b1c 100644 --- a/src/test/java/com/cta4j/bus/vehicle/internal/wire/CtaVehicleErrorTest.java +++ b/src/test/java/com/cta4j/bus/vehicle/internal/wire/CtaVehicleErrorTest.java @@ -25,4 +25,4 @@ void notFound_returnsFalse_whenNeitherVidNorRtPresent() { assertThat(error.notFound()).isFalse(); } -} \ No newline at end of file +} diff --git a/src/test/java/com/cta4j/bus/vehicle/model/TransitModeTest.java b/src/test/java/com/cta4j/bus/vehicle/model/TransitModeTest.java index 48051ca1..82fca855 100644 --- a/src/test/java/com/cta4j/bus/vehicle/model/TransitModeTest.java +++ b/src/test/java/com/cta4j/bus/vehicle/model/TransitModeTest.java @@ -24,4 +24,4 @@ void getCode_returnsCode() { assertThat(TransitMode.NONE.getCode()).isEqualTo(0); assertThat(TransitMode.BUS.getCode()).isEqualTo(1); } -} \ No newline at end of file +} diff --git a/src/test/java/com/cta4j/common/geo/CoordinatesTest.java b/src/test/java/com/cta4j/common/geo/CoordinatesTest.java index 6af5e468..becdbff5 100644 --- a/src/test/java/com/cta4j/common/geo/CoordinatesTest.java +++ b/src/test/java/com/cta4j/common/geo/CoordinatesTest.java @@ -48,4 +48,4 @@ void constructor_throwsIllegalArgumentException_whenHeadingTooHigh() { assertThatIllegalArgumentException().isThrownBy(() -> new Coordinates(new BigDecimal("41.8827"), new BigDecimal("-87.6233"), 360)); } -} \ No newline at end of file +} diff --git a/src/test/java/com/cta4j/train/TrainApiTest.java b/src/test/java/com/cta4j/train/TrainApiTest.java index c54d6e37..50f5f480 100644 --- a/src/test/java/com/cta4j/train/TrainApiTest.java +++ b/src/test/java/com/cta4j/train/TrainApiTest.java @@ -17,4 +17,4 @@ void builder_returnsWorkingInstance() { assertThat(api).isNotNull(); assertThat(api.stations()).isNotNull(); } -} \ No newline at end of file +} diff --git a/src/test/java/com/cta4j/train/arrival/exception/ArrivalsErrorCodeTest.java b/src/test/java/com/cta4j/train/arrival/exception/ArrivalsErrorCodeTest.java index f958ba9a..845b9b1e 100644 --- a/src/test/java/com/cta4j/train/arrival/exception/ArrivalsErrorCodeTest.java +++ b/src/test/java/com/cta4j/train/arrival/exception/ArrivalsErrorCodeTest.java @@ -22,4 +22,4 @@ void getCode_returnsCode() { assertThat(ArrivalsErrorCode.INVALID_API_KEY.getCode()).isEqualTo(101); assertThat(ArrivalsErrorCode.UNKNOWN.getCode()).isEqualTo(-1); } -} \ No newline at end of file +} diff --git a/src/test/java/com/cta4j/train/arrival/exception/Cta4jArrivalsExceptionTest.java b/src/test/java/com/cta4j/train/arrival/exception/Cta4jArrivalsExceptionTest.java index d4f7c056..61e00e8e 100644 --- a/src/test/java/com/cta4j/train/arrival/exception/Cta4jArrivalsExceptionTest.java +++ b/src/test/java/com/cta4j/train/arrival/exception/Cta4jArrivalsExceptionTest.java @@ -37,4 +37,4 @@ void constructor_setsUnknownErrorCode_whenRawErrorCodeIsUnrecognized() { assertThat(exception.getRawErrorCode()).isEqualTo(999); assertThat(exception.getErrorCode()).isEqualTo(ArrivalsErrorCode.UNKNOWN); } -} \ No newline at end of file +} diff --git a/src/test/java/com/cta4j/train/arrival/query/MapArrivalQueryTest.java b/src/test/java/com/cta4j/train/arrival/query/MapArrivalQueryTest.java index 4befcdb3..f6caf655 100644 --- a/src/test/java/com/cta4j/train/arrival/query/MapArrivalQueryTest.java +++ b/src/test/java/com/cta4j/train/arrival/query/MapArrivalQueryTest.java @@ -44,4 +44,4 @@ void constructor_throwsIllegalArgumentException_whenMaxResultsIsNotPositive() { assertThatIllegalArgumentException().isThrownBy(() -> new MapArrivalQuery("40900", null, 0)); } -} \ No newline at end of file +} diff --git a/src/test/java/com/cta4j/train/arrival/query/StopArrivalQueryTest.java b/src/test/java/com/cta4j/train/arrival/query/StopArrivalQueryTest.java index e78ac8f9..2004c60e 100644 --- a/src/test/java/com/cta4j/train/arrival/query/StopArrivalQueryTest.java +++ b/src/test/java/com/cta4j/train/arrival/query/StopArrivalQueryTest.java @@ -44,4 +44,4 @@ void constructor_throwsIllegalArgumentException_whenMaxResultsIsNotPositive() { assertThatIllegalArgumentException().isThrownBy(() -> new StopArrivalQuery("30070", null, 0)); } -} \ No newline at end of file +} diff --git a/src/test/java/com/cta4j/train/common/exception/Cta4jTrainExceptionTest.java b/src/test/java/com/cta4j/train/common/exception/Cta4jTrainExceptionTest.java index 10693b55..8c528842 100644 --- a/src/test/java/com/cta4j/train/common/exception/Cta4jTrainExceptionTest.java +++ b/src/test/java/com/cta4j/train/common/exception/Cta4jTrainExceptionTest.java @@ -36,4 +36,4 @@ void constructor_setsMessageEndpointAndRawErrorCode() { assertThat(exception.getCause()).isNull(); assertThat(exception.getRawErrorCode()).isEqualTo(101); } -} \ No newline at end of file +} diff --git a/src/test/java/com/cta4j/train/common/model/TrainDirectionTest.java b/src/test/java/com/cta4j/train/common/model/TrainDirectionTest.java index 4a4faf96..c28a3a2b 100644 --- a/src/test/java/com/cta4j/train/common/model/TrainDirectionTest.java +++ b/src/test/java/com/cta4j/train/common/model/TrainDirectionTest.java @@ -21,4 +21,4 @@ void getCode_returnsCode() { assertThat(TrainDirection.NORTHBOUND.getCode()).isEqualTo(1); assertThat(TrainDirection.SOUTHBOUND.getCode()).isEqualTo(5); } -} \ No newline at end of file +} diff --git a/src/test/java/com/cta4j/train/common/model/TrainLineTest.java b/src/test/java/com/cta4j/train/common/model/TrainLineTest.java index 3fb51290..00ed4e8a 100644 --- a/src/test/java/com/cta4j/train/common/model/TrainLineTest.java +++ b/src/test/java/com/cta4j/train/common/model/TrainLineTest.java @@ -36,4 +36,4 @@ void getCode_andGetColorHex_returnValues() { assertThat(TrainLine.RED.getCode()).isEqualTo("Red"); assertThat(TrainLine.RED.getColorHex()).isEqualTo("#C60C30"); } -} \ No newline at end of file +} diff --git a/src/test/java/com/cta4j/train/follow/exception/Cta4jFollowExceptionTest.java b/src/test/java/com/cta4j/train/follow/exception/Cta4jFollowExceptionTest.java index 4f1b1f21..e5817b53 100644 --- a/src/test/java/com/cta4j/train/follow/exception/Cta4jFollowExceptionTest.java +++ b/src/test/java/com/cta4j/train/follow/exception/Cta4jFollowExceptionTest.java @@ -37,4 +37,4 @@ void constructor_setsUnknownErrorCode_whenRawErrorCodeIsUnrecognized() { assertThat(exception.getRawErrorCode()).isEqualTo(999); assertThat(exception.getErrorCode()).isEqualTo(FollowErrorCode.UNKNOWN); } -} \ No newline at end of file +} diff --git a/src/test/java/com/cta4j/train/follow/exception/FollowErrorCodeTest.java b/src/test/java/com/cta4j/train/follow/exception/FollowErrorCodeTest.java index 431e69ba..30c7439a 100644 --- a/src/test/java/com/cta4j/train/follow/exception/FollowErrorCodeTest.java +++ b/src/test/java/com/cta4j/train/follow/exception/FollowErrorCodeTest.java @@ -22,4 +22,4 @@ void getCode_returnsCode() { assertThat(FollowErrorCode.RUN_NOT_FOUND.getCode()).isEqualTo(501); assertThat(FollowErrorCode.UNKNOWN.getCode()).isEqualTo(-1); } -} \ No newline at end of file +} diff --git a/src/test/java/com/cta4j/train/location/exception/Cta4jLocationsExceptionTest.java b/src/test/java/com/cta4j/train/location/exception/Cta4jLocationsExceptionTest.java index 3e59f433..2f64d52f 100644 --- a/src/test/java/com/cta4j/train/location/exception/Cta4jLocationsExceptionTest.java +++ b/src/test/java/com/cta4j/train/location/exception/Cta4jLocationsExceptionTest.java @@ -37,4 +37,4 @@ void constructor_setsUnknownErrorCode_whenRawErrorCodeIsUnrecognized() { assertThat(exception.getRawErrorCode()).isEqualTo(999); assertThat(exception.getErrorCode()).isEqualTo(LocationsErrorCode.UNKNOWN); } -} \ No newline at end of file +} diff --git a/src/test/java/com/cta4j/train/location/exception/LocationsErrorCodeTest.java b/src/test/java/com/cta4j/train/location/exception/LocationsErrorCodeTest.java index e0325e7e..e4ce1ebf 100644 --- a/src/test/java/com/cta4j/train/location/exception/LocationsErrorCodeTest.java +++ b/src/test/java/com/cta4j/train/location/exception/LocationsErrorCodeTest.java @@ -22,4 +22,4 @@ void getCode_returnsCode() { assertThat(LocationsErrorCode.INVALID_ROUTE.getCode()).isEqualTo(106); assertThat(LocationsErrorCode.UNKNOWN.getCode()).isEqualTo(-1); } -} \ No newline at end of file +} diff --git a/src/test/java/com/cta4j/train/station/model/CardinalDirectionTest.java b/src/test/java/com/cta4j/train/station/model/CardinalDirectionTest.java index 7b25db0f..bb429f64 100644 --- a/src/test/java/com/cta4j/train/station/model/CardinalDirectionTest.java +++ b/src/test/java/com/cta4j/train/station/model/CardinalDirectionTest.java @@ -21,4 +21,4 @@ void fromCode_returnsCorrectValues() { void fromCode_throwsIllegalArgumentException_whenCodeIsUnknown() { assertThatIllegalArgumentException().isThrownBy(() -> CardinalDirection.fromCode("X")); } -} \ No newline at end of file +} diff --git a/src/test/resources/alert/routestatus/bad_error_code.json b/src/test/resources/alert/routestatus/bad_error_code.json new file mode 100644 index 00000000..53b13a8b --- /dev/null +++ b/src/test/resources/alert/routestatus/bad_error_code.json @@ -0,0 +1,6 @@ +{ + "CTARoutes": { + "TimeStamp": "2026-07-17T14:40:15", + "ErrorCode": "notanumber" + } +} \ No newline at end of file diff --git a/src/test/resources/alert/routestatus/blank_error_message.json b/src/test/resources/alert/routestatus/blank_error_message.json new file mode 100644 index 00000000..945234d9 --- /dev/null +++ b/src/test/resources/alert/routestatus/blank_error_message.json @@ -0,0 +1,7 @@ +{ + "CTARoutes": { + "TimeStamp": "2026-07-17T14:40:15", + "ErrorCode": "900", + "ErrorMessage": "" + } +} \ No newline at end of file diff --git a/src/test/resources/alert/routestatus/bus_success.json b/src/test/resources/alert/routestatus/bus_success.json new file mode 100644 index 00000000..c012402e --- /dev/null +++ b/src/test/resources/alert/routestatus/bus_success.json @@ -0,0 +1,14 @@ +{ + "CTARoutes": { + "TimeStamp": "2026-07-17T14:10:43", + "RouteInfo": { + "Route": "Clark", + "RouteColorCode": "565a5c", + "RouteTextColor": "ffffff", + "ServiceId": "22", + "RouteURL": {"#cdata-section": "http://www.transitchicago.com/bus/22/"}, + "RouteStatus": "Bus Stop Note", + "RouteStatusColor": "000000" + } + } +} \ No newline at end of file diff --git a/src/test/resources/alert/routestatus/distinct_error_codes.json b/src/test/resources/alert/routestatus/distinct_error_codes.json new file mode 100644 index 00000000..73863f08 --- /dev/null +++ b/src/test/resources/alert/routestatus/distinct_error_codes.json @@ -0,0 +1,6 @@ +{ + "CTARoutes": { + "TimeStamp": "2026-07-17T14:40:15", + "ErrorCode": ["0", "50"] + } +} \ No newline at end of file diff --git a/src/test/resources/alert/routestatus/empty_route_info_array.json b/src/test/resources/alert/routestatus/empty_route_info_array.json new file mode 100644 index 00000000..8e210b04 --- /dev/null +++ b/src/test/resources/alert/routestatus/empty_route_info_array.json @@ -0,0 +1,8 @@ +{ + "CTARoutes": { + "TimeStamp": "2026-07-17T14:40:15", + "ErrorCode": "0", + "ErrorMessage": null, + "RouteInfo": [] + } +} \ No newline at end of file diff --git a/src/test/resources/alert/routestatus/error_code_empty_array.json b/src/test/resources/alert/routestatus/error_code_empty_array.json new file mode 100644 index 00000000..1b81a401 --- /dev/null +++ b/src/test/resources/alert/routestatus/error_code_empty_array.json @@ -0,0 +1,6 @@ +{ + "CTARoutes": { + "TimeStamp": "2026-07-17T14:40:15", + "ErrorCode": [] + } +} \ No newline at end of file diff --git a/src/test/resources/alert/routestatus/error_message_empty_array.json b/src/test/resources/alert/routestatus/error_message_empty_array.json new file mode 100644 index 00000000..0dab11b4 --- /dev/null +++ b/src/test/resources/alert/routestatus/error_message_empty_array.json @@ -0,0 +1,7 @@ +{ + "CTARoutes": { + "TimeStamp": "2026-07-17T14:40:15", + "ErrorCode": "101", + "ErrorMessage": [] + } +} \ No newline at end of file diff --git a/src/test/resources/alert/routestatus/error_no_message.json b/src/test/resources/alert/routestatus/error_no_message.json new file mode 100644 index 00000000..4ff40ff9 --- /dev/null +++ b/src/test/resources/alert/routestatus/error_no_message.json @@ -0,0 +1,6 @@ +{ + "CTARoutes": { + "TimeStamp": "2026-07-17T14:40:15", + "ErrorCode": "101" + } +} \ No newline at end of file diff --git a/src/test/resources/alert/routestatus/error_null_message_element.json b/src/test/resources/alert/routestatus/error_null_message_element.json new file mode 100644 index 00000000..800a0c60 --- /dev/null +++ b/src/test/resources/alert/routestatus/error_null_message_element.json @@ -0,0 +1,7 @@ +{ + "CTARoutes": { + "TimeStamp": "2026-07-17T14:40:15", + "ErrorCode": "101", + "ErrorMessage": [null] + } +} \ No newline at end of file diff --git a/src/test/resources/alert/routestatus/invalid_type_error.json b/src/test/resources/alert/routestatus/invalid_type_error.json new file mode 100644 index 00000000..0a9f1468 --- /dev/null +++ b/src/test/resources/alert/routestatus/invalid_type_error.json @@ -0,0 +1,7 @@ +{ + "CTARoutes": { + "TimeStamp": "2026-07-17T14:14:52", + "ErrorCode": "101", + "ErrorMessage": "Invalid option for parameter 'type': Valid options are 'bus', 'rail', 'station' or 'systemwide'" + } +} \ No newline at end of file diff --git a/src/test/resources/alert/routestatus/list_success.json b/src/test/resources/alert/routestatus/list_success.json new file mode 100644 index 00000000..44e48e53 --- /dev/null +++ b/src/test/resources/alert/routestatus/list_success.json @@ -0,0 +1,36 @@ +{ + "CTARoutes": { + "TimeStamp": "2026-07-17T14:06:58", + "ErrorCode": ["0", "0"], + "ErrorMessage": [null, null], + "RouteInfo": [ + { + "Route": "Red Line", + "RouteColorCode": "c60c30", + "RouteTextColor": "ffffff", + "ServiceId": "Red", + "RouteURL": {"#cdata-section": "http://www.transitchicago.com/redline/"}, + "RouteStatus": "Normal Service", + "RouteStatusColor": "404040" + }, + { + "Route": "Clark", + "RouteColorCode": "565a5c", + "RouteTextColor": "ffffff", + "ServiceId": "22", + "RouteURL": {"#cdata-section": "http://www.transitchicago.com/bus/22/"}, + "RouteStatus": "Bus Stop Note", + "RouteStatusColor": "000000" + }, + { + "Route": "All Routes", + "RouteColorCode": "000000", + "RouteTextColor": "ffffff", + "ServiceId": "Systemwide", + "RouteURL": {"#cdata-section": "http://www.transitchicago.com/travel_information/systemalerts.aspx"}, + "RouteStatus": "Normal Service", + "RouteStatusColor": "404040" + } + ] + } +} \ No newline at end of file diff --git a/src/test/resources/alert/routestatus/no_data_no_error.json b/src/test/resources/alert/routestatus/no_data_no_error.json new file mode 100644 index 00000000..9ca61e9e --- /dev/null +++ b/src/test/resources/alert/routestatus/no_data_no_error.json @@ -0,0 +1,5 @@ +{ + "CTARoutes": { + "TimeStamp": "2026-07-17T14:40:15" + } +} \ No newline at end of file diff --git a/src/test/resources/alert/routestatus/no_results.json b/src/test/resources/alert/routestatus/no_results.json new file mode 100644 index 00000000..39c1d8c2 --- /dev/null +++ b/src/test/resources/alert/routestatus/no_results.json @@ -0,0 +1,7 @@ +{ + "CTARoutes": { + "TimeStamp": "2026-07-17T14:40:15", + "ErrorCode": "50", + "ErrorMessage": "There are no routes based on your filter criteria" + } +} \ No newline at end of file diff --git a/src/test/resources/alert/routestatus/rail_success.json b/src/test/resources/alert/routestatus/rail_success.json new file mode 100644 index 00000000..84595a21 --- /dev/null +++ b/src/test/resources/alert/routestatus/rail_success.json @@ -0,0 +1,27 @@ +{ + "CTARoutes": { + "TimeStamp": "2026-07-17T14:14:52", + "ErrorCode": "0", + "ErrorMessage": null, + "RouteInfo": [ + { + "Route": "Red Line", + "RouteColorCode": "c60c30", + "RouteTextColor": "ffffff", + "ServiceId": "Red", + "RouteURL": {"#cdata-section": "http://www.transitchicago.com/redline/"}, + "RouteStatus": "Normal Service", + "RouteStatusColor": "404040" + }, + { + "Route": "Blue Line", + "RouteColorCode": "00a1de", + "RouteTextColor": "FFFFFF", + "ServiceId": "Blue", + "RouteURL": {"#cdata-section": "http://www.transitchicago.com/blueline/"}, + "RouteStatus": "Normal Service", + "RouteStatusColor": "404040" + } + ] + } +} \ No newline at end of file From 4e1611dc7dbdee5ce6bc5abedf5d70a98d2f4db0 Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Sat, 18 Jul 2026 12:09:24 -0500 Subject: [PATCH 08/60] GitHub Actions version bumps --- .github/workflows/build.yaml | 4 ++-- .github/workflows/release.yaml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index d82d7941..7f8cd8bd 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -18,10 +18,10 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Set up JDK - uses: actions/setup-java@v4 + uses: actions/setup-java@v6 with: distribution: "corretto" java-version: "21" diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index f65a5203..73781d38 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -17,13 +17,13 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v7 with: fetch-depth: 0 fetch-tags: true - name: Set up JDK - uses: actions/setup-java@v4 + uses: actions/setup-java@v6 with: distribution: corretto java-version: '21' From 3616b27308c817871277571765859534d870791f Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Sat, 18 Jul 2026 12:12:51 -0500 Subject: [PATCH 09/60] GitHub Actions version bumps --- .github/workflows/build.yaml | 8 ++++---- .github/workflows/release.yaml | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 7f8cd8bd..49b23e9b 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -21,11 +21,11 @@ jobs: uses: actions/checkout@v7 - name: Set up JDK - uses: actions/setup-java@v6 + uses: actions/setup-java@v5.6.0 with: - distribution: "corretto" - java-version: "21" - cache: "maven" + distribution: 'corretto' + java-version: '21' + cache: 'maven' - name: Read project version from pom.xml id: ver diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 73781d38..dd5f3f49 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -23,7 +23,7 @@ jobs: fetch-tags: true - name: Set up JDK - uses: actions/setup-java@v6 + uses: actions/setup-java@v5.6.0 with: distribution: corretto java-version: '21' From dedcc04fbd938192ad6a4eb0ebb966b4bb15b1d7 Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Sat, 18 Jul 2026 12:19:28 -0500 Subject: [PATCH 10/60] Address GitHub Copilot comments --- src/main/java/com/cta4j/alert/routestatus/RouteStatusApi.java | 4 ++++ .../alert/routestatus/internal/impl/RouteStatusApiImpl.java | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/cta4j/alert/routestatus/RouteStatusApi.java b/src/main/java/com/cta4j/alert/routestatus/RouteStatusApi.java index e4bafbef..40c43826 100644 --- a/src/main/java/com/cta4j/alert/routestatus/RouteStatusApi.java +++ b/src/main/java/com/cta4j/alert/routestatus/RouteStatusApi.java @@ -62,6 +62,8 @@ default List findByType(ServiceType type) { * @return a {@link List} of {@link RouteStatus}es associated with the bus route IDs, or an empty {@link List} if * no route statuses are found for the bus route IDs * @throws NullPointerException if {@code routeIds} is {@code null} or contains {@code null} elements + * @throws IllegalArgumentException if any of the {@code routeIds} matches a train line code (e.g. {@code "Red"}); + * use {@link #findByLines(Collection)} instead * @throws Cta4jRouteStatusException if the API returns an error response or the response cannot be parsed */ List findByBusRouteIds(Collection routeIds); @@ -73,6 +75,8 @@ default List findByType(ServiceType type) { * @return a {@link List} of {@link RouteStatus}es associated with the bus route ID, or an empty {@link List} if * no route statuses are found for the bus route ID * @throws NullPointerException if {@code routeId} is {@code null} + * @throws IllegalArgumentException if {@code routeId} matches a train line code (e.g. {@code "Red"}); use + * {@link #findByLine(TrainLine)} instead * @throws Cta4jRouteStatusException if the API returns an error response or the response cannot be parsed */ default List findByBusRouteId(String routeId) { diff --git a/src/main/java/com/cta4j/alert/routestatus/internal/impl/RouteStatusApiImpl.java b/src/main/java/com/cta4j/alert/routestatus/internal/impl/RouteStatusApiImpl.java index 51f4a800..1499f4a2 100644 --- a/src/main/java/com/cta4j/alert/routestatus/internal/impl/RouteStatusApiImpl.java +++ b/src/main/java/com/cta4j/alert/routestatus/internal/impl/RouteStatusApiImpl.java @@ -40,7 +40,7 @@ public final class RouteStatusApiImpl implements RouteStatusApi { private final AlertApiConfig config; public RouteStatusApiImpl(AlertApiConfig config) { - this.config = config; + this.config = Objects.requireNonNull(config); } @Override From 16e9929a515aafc23f1f7a13573ddbd6deb4f721 Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Sat, 18 Jul 2026 12:28:17 -0500 Subject: [PATCH 11/60] Add README badges --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index ba1fcc80..fb147b4d 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,11 @@ # cta4j Java SDK +[![Maven Central](https://img.shields.io/maven-central/v/com.cta4j/cta4j-java-sdk)](https://central.sonatype.com/artifact/com.cta4j/cta4j-java-sdk) +[![Build & Test](https://github.com/lbkulinski/cta4j-java-sdk/actions/workflows/build.yaml/badge.svg)](https://github.com/lbkulinski/cta4j-java-sdk/actions/workflows/build.yaml) +[![Javadoc](https://javadoc.io/badge2/com.cta4j/cta4j-java-sdk/javadoc.svg)](https://javadoc.io/doc/com.cta4j/cta4j-java-sdk) +![Java Version](https://img.shields.io/badge/Java-21%2B-orange) +[![License](https://img.shields.io/github/license/lbkulinski/cta4j-java-sdk)](LICENSE) + A lightweight Java SDK for interacting with the [Chicago Transit Authority (CTA)](https://www.transitchicago.com/) APIs — both Train Tracker and Bus Tracker. Built for simplicity, reliability, and minimal external dependencies. From 4702ea11b96b8cb650aa96ddcbde6081071005f8 Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Sat, 18 Jul 2026 12:31:33 -0500 Subject: [PATCH 12/60] gitignore updates --- .gitignore | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index a110e81b..6a0b2185 100644 --- a/.gitignore +++ b/.gitignore @@ -5,10 +5,7 @@ target/ .kotlin ### IntelliJ IDEA ### -.idea/modules.xml -.idea/jarRepositories.xml -.idea/compiler.xml -.idea/libraries/ +.idea/ *.iws *.iml *.ipr @@ -37,3 +34,6 @@ build/ ### Mac OS ### .DS_Store.superpowers/ + +### Misc ### +.env From f9a39f3f5823899e588ddd05fc081e48892a8580 Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Sat, 18 Jul 2026 20:51:07 -0500 Subject: [PATCH 13/60] Remove separate TrainRouteStatus model --- .../common/internal/mapper/Qualifiers.java | 14 ----- .../alert/routestatus/RouteStatusApi.java | 9 ++-- .../internal/impl/RouteStatusApiImpl.java | 19 +++---- .../mapper/TrainRouteStatusMapper.java | 23 -------- .../routestatus/model/TrainRouteStatus.java | 52 ------------------- .../alert/common/AlertQualifiersTest.java | 13 ----- .../routestatus/RouteStatusApiImplTest.java | 15 +++--- .../TrainRouteStatusMapperTest.java | 42 --------------- 8 files changed, 19 insertions(+), 168 deletions(-) delete mode 100644 src/main/java/com/cta4j/alert/routestatus/internal/mapper/TrainRouteStatusMapper.java delete mode 100644 src/main/java/com/cta4j/alert/routestatus/model/TrainRouteStatus.java delete mode 100644 src/test/java/com/cta4j/alert/routestatus/TrainRouteStatusMapperTest.java diff --git a/src/main/java/com/cta4j/alert/common/internal/mapper/Qualifiers.java b/src/main/java/com/cta4j/alert/common/internal/mapper/Qualifiers.java index 9e11ea36..0e730329 100644 --- a/src/main/java/com/cta4j/alert/common/internal/mapper/Qualifiers.java +++ b/src/main/java/com/cta4j/alert/common/internal/mapper/Qualifiers.java @@ -1,6 +1,5 @@ package com.cta4j.alert.common.internal.mapper; -import com.cta4j.common.train.TrainLine; import org.jetbrains.annotations.ApiStatus; import org.jspecify.annotations.NullMarked; import org.mapstruct.Named; @@ -28,17 +27,4 @@ public static URI mapUri(String value) { throw new IllegalArgumentException(message, e); } } - - @Named("mapTrainLine") - public static TrainLine mapTrainLine(String code) { - Objects.requireNonNull(code); - - try { - return TrainLine.fromCode(code); - } catch (IllegalArgumentException e) { - String message = "Failed to parse train line code: %s".formatted(code); - - throw new IllegalArgumentException(message, e); - } - } } diff --git a/src/main/java/com/cta4j/alert/routestatus/RouteStatusApi.java b/src/main/java/com/cta4j/alert/routestatus/RouteStatusApi.java index 40c43826..90ee93fe 100644 --- a/src/main/java/com/cta4j/alert/routestatus/RouteStatusApi.java +++ b/src/main/java/com/cta4j/alert/routestatus/RouteStatusApi.java @@ -3,7 +3,6 @@ import com.cta4j.alert.routestatus.exception.Cta4jRouteStatusException; import com.cta4j.alert.routestatus.model.RouteStatus; import com.cta4j.alert.routestatus.model.ServiceType; -import com.cta4j.alert.routestatus.model.TrainRouteStatus; import com.cta4j.common.train.TrainLine; import org.jspecify.annotations.NullMarked; @@ -91,23 +90,23 @@ default List findByBusRouteId(String routeId) { * Retrieves route statuses for the specified train lines. * * @param lines a {@link Collection} of train lines - * @return a {@link List} of {@link TrainRouteStatus}es associated with the train lines, or an empty {@link List} + * @return a {@link List} of {@link RouteStatus}es associated with the train lines, or an empty {@link List} * if no route statuses are found for the train lines * @throws NullPointerException if {@code lines} is {@code null} or contains {@code null} elements * @throws Cta4jRouteStatusException if the API returns an error response or the response cannot be parsed */ - List findByLines(Collection lines); + List findByLines(Collection lines); /** * Retrieves route statuses for the specified train line. * * @param line the train line - * @return a {@link List} of {@link TrainRouteStatus}es associated with the train line, or an empty {@link List} + * @return a {@link List} of {@link RouteStatus}es associated with the train line, or an empty {@link List} * if no route statuses are found for the train line * @throws NullPointerException if {@code line} is {@code null} * @throws Cta4jRouteStatusException if the API returns an error response or the response cannot be parsed */ - default List findByLine(TrainLine line) { + default List findByLine(TrainLine line) { Objects.requireNonNull(line); List lines = List.of(line); diff --git a/src/main/java/com/cta4j/alert/routestatus/internal/impl/RouteStatusApiImpl.java b/src/main/java/com/cta4j/alert/routestatus/internal/impl/RouteStatusApiImpl.java index 1499f4a2..7dff4a10 100644 --- a/src/main/java/com/cta4j/alert/routestatus/internal/impl/RouteStatusApiImpl.java +++ b/src/main/java/com/cta4j/alert/routestatus/internal/impl/RouteStatusApiImpl.java @@ -6,13 +6,11 @@ import com.cta4j.alert.routestatus.exception.Cta4jRouteStatusException; import com.cta4j.alert.routestatus.exception.RouteStatusErrorCode; import com.cta4j.alert.routestatus.internal.mapper.RouteStatusMapper; -import com.cta4j.alert.routestatus.internal.mapper.TrainRouteStatusMapper; import com.cta4j.alert.routestatus.internal.wire.CtaRouteInfo; import com.cta4j.alert.routestatus.internal.wire.CtaRouteStatusResponse; import com.cta4j.alert.routestatus.internal.wire.CtaRoutes; import com.cta4j.alert.routestatus.model.RouteStatus; import com.cta4j.alert.routestatus.model.ServiceType; -import com.cta4j.alert.routestatus.model.TrainRouteStatus; import com.cta4j.common.train.TrainLine; import org.apache.hc.client5.http.fluent.Request; import org.apache.hc.core5.net.URIBuilder; @@ -29,7 +27,6 @@ import java.util.Collection; import java.util.List; import java.util.Objects; -import java.util.function.Function; import java.util.stream.Collectors; @ApiStatus.Internal @@ -53,7 +50,7 @@ public List list() { .addParameter("outputType", "JSON") .toString(); - return this.makeRequest(url, RouteStatusMapper.INSTANCE::toDomain); + return this.makeRequest(url); } @Override @@ -80,7 +77,7 @@ public List findByTypes(Collection types) { .addParameter("outputType", "JSON") .toString(); - return this.makeRequest(url, RouteStatusMapper.INSTANCE::toDomain); + return this.makeRequest(url); } @Override @@ -114,11 +111,11 @@ public List findByBusRouteIds(Collection routeIds) { .addParameter("outputType", "JSON") .toString(); - return this.makeRequest(url, RouteStatusMapper.INSTANCE::toDomain); + return this.makeRequest(url); } @Override - public List findByLines(Collection lines) { + public List findByLines(Collection lines) { Objects.requireNonNull(lines); List linesList = List.copyOf(lines); @@ -140,7 +137,7 @@ public List findByLines(Collection lines) { .addParameter("outputType", "JSON") .toString(); - return this.makeRequest(url, TrainRouteStatusMapper.INSTANCE::toDomain); + return this.makeRequest(url); } @Override @@ -156,10 +153,10 @@ public List findByStationId(String stationId) { .addParameter("outputType", "JSON") .toString(); - return this.makeRequest(url, RouteStatusMapper.INSTANCE::toDomain); + return this.makeRequest(url); } - private List makeRequest(String url, Function mapper) { + private List makeRequest(String url) { String response; try { @@ -188,7 +185,7 @@ private List makeRequest(String url, Function mapper) { if (routeInfo != null && !routeInfo.isEmpty()) { return routeInfo.stream() - .map(mapper) + .map(RouteStatusMapper.INSTANCE::toDomain) .toList(); } diff --git a/src/main/java/com/cta4j/alert/routestatus/internal/mapper/TrainRouteStatusMapper.java b/src/main/java/com/cta4j/alert/routestatus/internal/mapper/TrainRouteStatusMapper.java deleted file mode 100644 index fedb6b49..00000000 --- a/src/main/java/com/cta4j/alert/routestatus/internal/mapper/TrainRouteStatusMapper.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.cta4j.alert.routestatus.internal.mapper; - -import com.cta4j.alert.common.internal.mapper.Qualifiers; -import com.cta4j.alert.routestatus.internal.wire.CtaRouteInfo; -import com.cta4j.alert.routestatus.model.TrainRouteStatus; -import org.jetbrains.annotations.ApiStatus; -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.factory.Mappers; - -@Mapper(uses = Qualifiers.class) -@ApiStatus.Internal -public interface TrainRouteStatusMapper { - TrainRouteStatusMapper INSTANCE = Mappers.getMapper(TrainRouteStatusMapper.class); - - @Mapping(target = "color", source = "routeColorCode") - @Mapping(target = "textColor", source = "routeTextColor") - @Mapping(target = "url", source = "routeUrl.cdataSection", qualifiedByName = "mapUri") - @Mapping(target = "line", source = "serviceId", qualifiedByName = "mapTrainLine") - @Mapping(target = "status", source = "routeStatus") - @Mapping(target = "statusColor", source = "routeStatusColor") - TrainRouteStatus toDomain(CtaRouteInfo routeInfo); -} diff --git a/src/main/java/com/cta4j/alert/routestatus/model/TrainRouteStatus.java b/src/main/java/com/cta4j/alert/routestatus/model/TrainRouteStatus.java deleted file mode 100644 index 66ec9cfc..00000000 --- a/src/main/java/com/cta4j/alert/routestatus/model/TrainRouteStatus.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.cta4j.alert.routestatus.model; - -import com.cta4j.common.train.TrainLine; -import org.jspecify.annotations.NullMarked; - -import java.net.URI; -import java.util.Objects; - -/** - * Represents the service status of a single train route. - * - * @param route the name of this route (e.g., "Red Line") - * @param color the color of this route used in maps, as {@code rrggbb} (e.g., "c60c30") - * @param textColor the suggested color of text displayed against {@code color}, as {@code rrggbb} (e.g., "ffffff") - * @param line the {@link TrainLine} this status corresponds to - * @param url the URL of this route's page on transitchicago.com - * @param status the ultimate, human-readable status of this route (e.g., "Normal Service", "Service Change") - * @param statusColor the suggested color associated with {@code status}, as {@code rrggbb} (e.g., "404040") - */ -@NullMarked -public record TrainRouteStatus( - String route, - String color, - String textColor, - TrainLine line, - URI url, - String status, - String statusColor -) { - /** - * Constructs a {@code TrainRouteStatus}. - * - * @param route the name of the route (e.g., "Red Line") - * @param color the color of the route used in maps, as {@code rrggbb} (e.g., "c60c30") - * @param textColor the suggested color of text displayed against {@code color}, as {@code rrggbb} (e.g., "ffffff") - * @param line the {@link TrainLine} the route corresponds to - * @param url the URL of the route's page on transitchicago.com - * @param status the ultimate, human-readable status of the route (e.g., "Normal Service", "Service Change") - * @param statusColor the suggested color associated with {@code status}, as {@code rrggbb} (e.g., "404040") - * @throws NullPointerException if {@code route}, {@code color}, {@code textColor}, {@code line}, - * {@code url}, {@code status}, or {@code statusColor} is {@code null} - */ - public TrainRouteStatus { - Objects.requireNonNull(route); - Objects.requireNonNull(color); - Objects.requireNonNull(textColor); - Objects.requireNonNull(line); - Objects.requireNonNull(url); - Objects.requireNonNull(status); - Objects.requireNonNull(statusColor); - } -} diff --git a/src/test/java/com/cta4j/alert/common/AlertQualifiersTest.java b/src/test/java/com/cta4j/alert/common/AlertQualifiersTest.java index 1c2e6a47..e1982426 100644 --- a/src/test/java/com/cta4j/alert/common/AlertQualifiersTest.java +++ b/src/test/java/com/cta4j/alert/common/AlertQualifiersTest.java @@ -1,7 +1,6 @@ package com.cta4j.alert.common; import com.cta4j.alert.common.internal.mapper.Qualifiers; -import com.cta4j.common.train.TrainLine; import org.junit.jupiter.api.Test; import java.net.URI; @@ -22,16 +21,4 @@ void mapUri_throwsIllegalArgumentException_whenValueIsInvalid() { .withMessageContaining("Failed to parse URI") .withCauseInstanceOf(java.net.URISyntaxException.class); } - - @Test - void mapTrainLine_returnsTrainLine_whenCodeIsValid() { - assertThat(Qualifiers.mapTrainLine("Red")).isEqualTo(TrainLine.RED); - } - - @Test - void mapTrainLine_throwsIllegalArgumentException_whenCodeIsInvalid() { - assertThatIllegalArgumentException().isThrownBy(() -> Qualifiers.mapTrainLine("22")) - .withMessage("Failed to parse train line code: 22") - .withCauseInstanceOf(IllegalArgumentException.class); - } } diff --git a/src/test/java/com/cta4j/alert/routestatus/RouteStatusApiImplTest.java b/src/test/java/com/cta4j/alert/routestatus/RouteStatusApiImplTest.java index 19b91540..d4c9e11a 100644 --- a/src/test/java/com/cta4j/alert/routestatus/RouteStatusApiImplTest.java +++ b/src/test/java/com/cta4j/alert/routestatus/RouteStatusApiImplTest.java @@ -8,7 +8,6 @@ import com.cta4j.alert.routestatus.internal.impl.RouteStatusApiImpl; import com.cta4j.alert.routestatus.model.RouteStatus; import com.cta4j.alert.routestatus.model.ServiceType; -import com.cta4j.alert.routestatus.model.TrainRouteStatus; import com.cta4j.common.train.TrainLine; import com.github.tomakehurst.wiremock.WireMockServer; import org.junit.jupiter.api.AfterEach; @@ -352,7 +351,7 @@ void findByBusRouteId_delegatesToFindByBusRouteIds() { @Test void findByLines_returnsEmpty_whenInputIsEmpty() { - List statuses = this.api.findByLines(List.of()); + List statuses = this.api.findByLines(List.of()); assertThat(statuses).isEmpty(); this.server.verify(0, anyRequestedFor(anyUrl())); @@ -367,24 +366,24 @@ void findByLines_sendsRouteidParameter_asCommaJoinedCodes() { .withHeader("Content-Type", "application/json") .withBody(TestFixtures.read("alert/routestatus/rail_success.json")))); - List statuses = this.api.findByLines(List.of(TrainLine.RED, TrainLine.BLUE)); + List statuses = this.api.findByLines(List.of(TrainLine.RED, TrainLine.BLUE)); assertThat(statuses).hasSize(2); } @Test - void findByLines_returnsTrainRouteStatuses_whenResponseContainsData() { + void findByLines_returnsRouteStatuses_whenResponseContainsData() { this.server.stubFor(get(urlPathEqualTo("/api/1.0/routes.aspx")) .willReturn(aResponse() .withStatus(200) .withHeader("Content-Type", "application/json") .withBody(TestFixtures.read("alert/routestatus/rail_success.json")))); - List statuses = this.api.findByLines(List.of(TrainLine.RED, TrainLine.BLUE)); + List statuses = this.api.findByLines(List.of(TrainLine.RED, TrainLine.BLUE)); assertThat(statuses).hasSize(2); - TrainRouteStatus redLine = statuses.getFirst(); - assertThat(redLine.line()).isEqualTo(TrainLine.RED); + RouteStatus redLine = statuses.getFirst(); + assertThat(redLine.serviceId()).isEqualTo("Red"); assertThat(redLine.route()).isEqualTo("Red Line"); assertThat(redLine.status()).isEqualTo("Normal Service"); } @@ -398,7 +397,7 @@ void findByLine_delegatesToFindByLines() { .withHeader("Content-Type", "application/json") .withBody(TestFixtures.read("alert/routestatus/rail_success.json")))); - List statuses = this.api.findByLine(TrainLine.RED); + List statuses = this.api.findByLine(TrainLine.RED); assertThat(statuses).hasSize(2); } diff --git a/src/test/java/com/cta4j/alert/routestatus/TrainRouteStatusMapperTest.java b/src/test/java/com/cta4j/alert/routestatus/TrainRouteStatusMapperTest.java deleted file mode 100644 index 028dc63b..00000000 --- a/src/test/java/com/cta4j/alert/routestatus/TrainRouteStatusMapperTest.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.cta4j.alert.routestatus; - -import com.cta4j.alert.routestatus.internal.mapper.TrainRouteStatusMapper; -import com.cta4j.alert.routestatus.internal.wire.CtaRouteInfo; -import com.cta4j.alert.routestatus.internal.wire.CtaRouteInfoUrl; -import com.cta4j.alert.routestatus.model.TrainRouteStatus; -import com.cta4j.common.train.TrainLine; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.*; - -class TrainRouteStatusMapperTest { - @Test - void toDomain_mapsAllFields() { - CtaRouteInfo wire = new CtaRouteInfo( - "Red Line", "c60c30", "ffffff", "Red", - new CtaRouteInfoUrl("http://www.transitchicago.com/redline/"), - "Normal Service", "404040" - ); - - TrainRouteStatus status = TrainRouteStatusMapper.INSTANCE.toDomain(wire); - - assertThat(status.route()).isEqualTo("Red Line"); - assertThat(status.color()).isEqualTo("c60c30"); - assertThat(status.textColor()).isEqualTo("ffffff"); - assertThat(status.line()).isEqualTo(TrainLine.RED); - assertThat(status.url()).hasToString("http://www.transitchicago.com/redline/"); - assertThat(status.status()).isEqualTo("Normal Service"); - assertThat(status.statusColor()).isEqualTo("404040"); - } - - @Test - void toDomain_throwsIllegalArgumentException_whenServiceIdIsNotATrainLine() { - CtaRouteInfo wire = new CtaRouteInfo( - "Clark", "565a5c", "ffffff", "22", - new CtaRouteInfoUrl("http://www.transitchicago.com/bus/22/"), - "Bus Stop Note", "000000" - ); - - assertThatIllegalArgumentException().isThrownBy(() -> TrainRouteStatusMapper.INSTANCE.toDomain(wire)); - } -} From 7158160c7d2b38dcd71fba95141c07344c5350ce Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Sun, 19 Jul 2026 11:47:09 -0500 Subject: [PATCH 14/60] Moved ServiceType into a common package and added AlertQuery --- .../model/ServiceType.java | 4 +- .../cta4j/alert/detail/query/AlertQuery.java | 185 ++++++++++++++++++ .../alert/routestatus/RouteStatusApi.java | 2 +- .../internal/impl/RouteStatusApiImpl.java | 2 +- .../train/arrival/query/MapArrivalQuery.java | 5 +- .../train/arrival/query/StopArrivalQuery.java | 5 +- .../routestatus/RouteStatusApiImplTest.java | 2 +- 7 files changed, 192 insertions(+), 13 deletions(-) rename src/main/java/com/cta4j/alert/{routestatus => common}/model/ServiceType.java (73%) create mode 100644 src/main/java/com/cta4j/alert/detail/query/AlertQuery.java diff --git a/src/main/java/com/cta4j/alert/routestatus/model/ServiceType.java b/src/main/java/com/cta4j/alert/common/model/ServiceType.java similarity index 73% rename from src/main/java/com/cta4j/alert/routestatus/model/ServiceType.java rename to src/main/java/com/cta4j/alert/common/model/ServiceType.java index 7707d449..38fb3ffe 100644 --- a/src/main/java/com/cta4j/alert/routestatus/model/ServiceType.java +++ b/src/main/java/com/cta4j/alert/common/model/ServiceType.java @@ -1,9 +1,9 @@ -package com.cta4j.alert.routestatus.model; +package com.cta4j.alert.common.model; import org.jspecify.annotations.NullMarked; /** - * Represents the type of service to filter route statuses by. + * Represents a category of CTA service — a bus route, train route, train station, or systemwide grouping. */ @NullMarked public enum ServiceType { diff --git a/src/main/java/com/cta4j/alert/detail/query/AlertQuery.java b/src/main/java/com/cta4j/alert/detail/query/AlertQuery.java new file mode 100644 index 00000000..214cbe30 --- /dev/null +++ b/src/main/java/com/cta4j/alert/detail/query/AlertQuery.java @@ -0,0 +1,185 @@ +package com.cta4j.alert.detail.query; + +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +import java.time.LocalDate; +import java.util.Objects; + +/** + * Represents a query for detailed alert information. + * + * @param activeOnly whether to include only alerts that are currently active + * @param accessibility whether to include alerts that affect accessible paths in stations + * @param planned whether to include common planned alerts + * @param byStartDate the optional date; only alerts with a start date before this date are included + * @param recentDays the optional number of days; only alerts that started within this many days of today are + * included + */ +@NullMarked +public record AlertQuery( + boolean activeOnly, + boolean accessibility, + boolean planned, + @Nullable LocalDate byStartDate, + @Nullable Integer recentDays +) { + /** + * Constructs an {@code AlertQuery}. + * + * @param activeOnly whether to include only alerts that are currently active + * @param accessibility whether to include alerts that affect accessible paths in stations + * @param planned whether to include common planned alerts + * @param byStartDate the optional date; only alerts with a start date before this date are included + * @param recentDays the optional number of days; only alerts that started within this many days of today are + * included + * @throws IllegalArgumentException if both {@code byStartDate} and {@code recentDays} are specified, or if + * {@code recentDays} is non-{@code null} and not positive + */ + public AlertQuery { + if (byStartDate != null && recentDays != null) { + throw new IllegalArgumentException("byStartDate and recentDays cannot both be specified"); + } + + if (recentDays != null && recentDays <= 0) { + throw new IllegalArgumentException("recentDays must be positive"); + } + } + + /** + * Creates a builder for {@code AlertQuery}. + * + * @return a new {@code Builder} instance + */ + public static Builder builder() { + return new Builder(); + } + + /** + * A builder for {@code AlertQuery}. + */ + public static final class Builder { + /** + * Whether to include only alerts that are currently active. + */ + private boolean activeOnly; + + /** + * Whether to include alerts that affect accessible paths in stations. + */ + private boolean accessibility; + + /** + * Whether to include common planned alerts. + */ + private boolean planned; + + /** + * The optional date; only alerts with a start date before this date are included. + */ + @Nullable + private LocalDate byStartDate; + + /** + * The optional number of days; only alerts that started within this many days of today are included. + */ + @Nullable + private Integer recentDays; + + /** + * Constructs a {@code Builder}. + *

+ * By default, {@code activeOnly} is {@code false}, and {@code accessibility} and {@code planned} are + * {@code true}, matching the CTA Alerts API's own defaults. + */ + public Builder() { + this.activeOnly = false; + this.accessibility = true; + this.planned = true; + } + + /** + * Sets whether to include only alerts that are currently active. + * + * @param activeOnly whether to include only active alerts + * @return this {@code Builder} instance + */ + public Builder activeOnly(boolean activeOnly) { + this.activeOnly = activeOnly; + + return this; + } + + /** + * Sets whether to include alerts that affect accessible paths in stations. + * + * @param accessibility whether to include accessibility-related alerts + * @return this {@code Builder} instance + */ + public Builder accessibility(boolean accessibility) { + this.accessibility = accessibility; + + return this; + } + + /** + * Sets whether to include common planned alerts. + * + * @param planned whether to include planned alerts + * @return this {@code Builder} instance + */ + public Builder planned(boolean planned) { + this.planned = planned; + + return this; + } + + /** + * Sets the date; only alerts with a start date before this date are included. + * + * @param byStartDate the date to filter alerts by + * @return this {@code Builder} instance + * @throws NullPointerException if {@code byStartDate} is {@code null} + */ + public Builder byStartDate(LocalDate byStartDate) { + Objects.requireNonNull(byStartDate); + + this.byStartDate = byStartDate; + + return this; + } + + /** + * Sets the number of days; only alerts that started within this many days of today are included. + * + * @param recentDays the number of days to filter alerts by + * @return this {@code Builder} instance + * @throws IllegalArgumentException if {@code recentDays} is not positive + */ + public Builder recentDays(int recentDays) { + if (recentDays <= 0) { + throw new IllegalArgumentException("recentDays must be positive"); + } + + this.recentDays = recentDays; + + return this; + } + + /** + * Builds the {@code AlertQuery}. + * + * @return a new {@code AlertQuery} instance + * @throws IllegalArgumentException if both {@code byStartDate} and {@code recentDays} were specified + */ + public AlertQuery build() { + return new AlertQuery( + this.activeOnly, + this.accessibility, + this.planned, + this.byStartDate, + this.recentDays + ); + } + } +} diff --git a/src/main/java/com/cta4j/alert/routestatus/RouteStatusApi.java b/src/main/java/com/cta4j/alert/routestatus/RouteStatusApi.java index 90ee93fe..b21838c4 100644 --- a/src/main/java/com/cta4j/alert/routestatus/RouteStatusApi.java +++ b/src/main/java/com/cta4j/alert/routestatus/RouteStatusApi.java @@ -2,7 +2,7 @@ import com.cta4j.alert.routestatus.exception.Cta4jRouteStatusException; import com.cta4j.alert.routestatus.model.RouteStatus; -import com.cta4j.alert.routestatus.model.ServiceType; +import com.cta4j.alert.common.model.ServiceType; import com.cta4j.common.train.TrainLine; import org.jspecify.annotations.NullMarked; diff --git a/src/main/java/com/cta4j/alert/routestatus/internal/impl/RouteStatusApiImpl.java b/src/main/java/com/cta4j/alert/routestatus/internal/impl/RouteStatusApiImpl.java index 7dff4a10..218606c5 100644 --- a/src/main/java/com/cta4j/alert/routestatus/internal/impl/RouteStatusApiImpl.java +++ b/src/main/java/com/cta4j/alert/routestatus/internal/impl/RouteStatusApiImpl.java @@ -10,7 +10,7 @@ import com.cta4j.alert.routestatus.internal.wire.CtaRouteStatusResponse; import com.cta4j.alert.routestatus.internal.wire.CtaRoutes; import com.cta4j.alert.routestatus.model.RouteStatus; -import com.cta4j.alert.routestatus.model.ServiceType; +import com.cta4j.alert.common.model.ServiceType; import com.cta4j.common.train.TrainLine; import org.apache.hc.client5.http.fluent.Request; import org.apache.hc.core5.net.URIBuilder; diff --git a/src/main/java/com/cta4j/train/arrival/query/MapArrivalQuery.java b/src/main/java/com/cta4j/train/arrival/query/MapArrivalQuery.java index 7c44b191..a1ebdca8 100644 --- a/src/main/java/com/cta4j/train/arrival/query/MapArrivalQuery.java +++ b/src/main/java/com/cta4j/train/arrival/query/MapArrivalQuery.java @@ -96,12 +96,9 @@ public Builder line(TrainLine line) { * * @param maxResults the maximum number of arrival information * @return this {@code Builder} instance - * @throws NullPointerException if {@code maxResults} is {@code null} * @throws IllegalArgumentException if {@code maxResults} is not positive */ - public Builder maxResults(Integer maxResults) { - Objects.requireNonNull(maxResults); - + public Builder maxResults(int maxResults) { if (maxResults <= 0) { throw new IllegalArgumentException("maxResults must be positive"); } diff --git a/src/main/java/com/cta4j/train/arrival/query/StopArrivalQuery.java b/src/main/java/com/cta4j/train/arrival/query/StopArrivalQuery.java index e0796226..acc46194 100644 --- a/src/main/java/com/cta4j/train/arrival/query/StopArrivalQuery.java +++ b/src/main/java/com/cta4j/train/arrival/query/StopArrivalQuery.java @@ -96,12 +96,9 @@ public Builder line(TrainLine line) { * * @param maxResults the maximum number of arrival information * @return this {@code Builder} instance - * @throws NullPointerException if {@code maxResults} is {@code null} * @throws IllegalArgumentException if {@code maxResults} is not positive */ - public Builder maxResults(Integer maxResults) { - Objects.requireNonNull(maxResults); - + public Builder maxResults(int maxResults) { if (maxResults <= 0) { throw new IllegalArgumentException("maxResults must be positive"); } diff --git a/src/test/java/com/cta4j/alert/routestatus/RouteStatusApiImplTest.java b/src/test/java/com/cta4j/alert/routestatus/RouteStatusApiImplTest.java index d4c9e11a..c8100204 100644 --- a/src/test/java/com/cta4j/alert/routestatus/RouteStatusApiImplTest.java +++ b/src/test/java/com/cta4j/alert/routestatus/RouteStatusApiImplTest.java @@ -7,7 +7,7 @@ import com.cta4j.alert.routestatus.exception.RouteStatusErrorCode; import com.cta4j.alert.routestatus.internal.impl.RouteStatusApiImpl; import com.cta4j.alert.routestatus.model.RouteStatus; -import com.cta4j.alert.routestatus.model.ServiceType; +import com.cta4j.alert.common.model.ServiceType; import com.cta4j.common.train.TrainLine; import com.github.tomakehurst.wiremock.WireMockServer; import org.junit.jupiter.api.AfterEach; From 75dc9e28ea914d0567ba6b2a50e95db871fc77cc Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Sun, 19 Jul 2026 12:37:34 -0500 Subject: [PATCH 15/60] Update Javadoc comments to replace "arrival information" with "arrivals" --- .../com/cta4j/train/arrival/ArrivalsApi.java | 14 ++++----- .../train/arrival/query/MapArrivalQuery.java | 30 +++++++++---------- .../train/arrival/query/StopArrivalQuery.java | 30 +++++++++---------- 3 files changed, 37 insertions(+), 37 deletions(-) diff --git a/src/main/java/com/cta4j/train/arrival/ArrivalsApi.java b/src/main/java/com/cta4j/train/arrival/ArrivalsApi.java index b4caf27d..1bf5fab0 100644 --- a/src/main/java/com/cta4j/train/arrival/ArrivalsApi.java +++ b/src/main/java/com/cta4j/train/arrival/ArrivalsApi.java @@ -11,14 +11,14 @@ /** * Provides access to arrival-related endpoints of the CTA Train Tracker API. *

- * This API allows retrieval of arrival information by map ID or stop ID. + * This API allows retrieval of arrivals by map ID or stop ID. */ @NullMarked public interface ArrivalsApi { /** - * Retrieves arrival information by map ID. + * Retrieves arrivals by map ID. * - * @param query the query parameters for fetching arrival information by map ID + * @param query the query parameters for fetching arrivals by map ID * @return a {@link List} of {@link Arrival}s corresponding to the provided map ID, or an empty {@link List} if no * arrivals are found * @throws NullPointerException if {@code query} is {@code null} @@ -27,9 +27,9 @@ public interface ArrivalsApi { List findByMapId(MapArrivalQuery query); /** - * Retrieves arrival information by stop ID. + * Retrieves arrivals by stop ID. * - * @param query the query parameters for fetching arrival information by stop ID + * @param query the query parameters for fetching arrivals by stop ID * @return a {@link List} of {@link Arrival}s corresponding to the provided stop ID, or an empty {@link List} if no * arrivals are found * @throws NullPointerException if {@code query} is {@code null} @@ -38,7 +38,7 @@ public interface ArrivalsApi { List findByStopId(StopArrivalQuery query); /** - * Retrieves arrival information by map ID. + * Retrieves arrivals by map ID. * * @param mapId the map ID * @return a {@link List} of {@link Arrival}s corresponding to the provided map ID, or an empty {@link List} if no @@ -54,7 +54,7 @@ default List findByMapId(String mapId) { } /** - * Retrieves arrival information by stop ID. + * Retrieves arrivals by stop ID. * * @param stopId the stop ID * @return a {@link List} of {@link Arrival}s corresponding to the provided stop ID, or an empty {@link List} if no diff --git a/src/main/java/com/cta4j/train/arrival/query/MapArrivalQuery.java b/src/main/java/com/cta4j/train/arrival/query/MapArrivalQuery.java index a1ebdca8..d1729500 100644 --- a/src/main/java/com/cta4j/train/arrival/query/MapArrivalQuery.java +++ b/src/main/java/com/cta4j/train/arrival/query/MapArrivalQuery.java @@ -7,11 +7,11 @@ import java.util.Objects; /** - * Represents a query for train arrival information for a specific map. + * Represents a query for train arrivals at a specific map. * - * @param mapId the ID of the map to retrieve arrival information for - * @param line the optional train line to filter arrival information by - * @param maxResults the optional maximum number of arrival information to return + * @param mapId the ID of the map to retrieve arrivals for + * @param line the optional train line to filter arrivals by + * @param maxResults the optional maximum number of arrivals to return */ @NullMarked public record MapArrivalQuery( @@ -22,9 +22,9 @@ public record MapArrivalQuery( /** * Constructs a {@code MapArrivalQuery}. * - * @param mapId the ID of the map to retrieve arrival information for - * @param line the optional train line to filter arrival information by - * @param maxResults the optional maximum number of arrival information to return + * @param mapId the ID of the map to retrieve arrivals for + * @param line the optional train line to filter arrivals by + * @param maxResults the optional maximum number of arrivals to return * @throws NullPointerException if {@code mapId} is {@code null} * @throws IllegalArgumentException if {@code maxResults} is non-{@code null} and not positive */ @@ -39,7 +39,7 @@ public record MapArrivalQuery( /** * Creates a builder for {@code MapArrivalQuery}. * - * @param mapId the ID of the map to retrieve arrival information for + * @param mapId the ID of the map to retrieve arrivals for * @return a new {@code Builder} instance * @throws NullPointerException if {@code mapId} is {@code null} */ @@ -52,18 +52,18 @@ public static Builder builder(String mapId) { */ public static final class Builder { /** - * The ID of the map to retrieve arrival information for. + * The ID of the map to retrieve arrivals for. */ private final String mapId; /** - * The optional train line to filter arrival information by. + * The optional train line to filter arrivals by. */ @Nullable private TrainLine line; /** - * The optional maximum number of arrival information to return. + * The optional maximum number of arrivals to return. */ @Nullable private Integer maxResults; @@ -71,7 +71,7 @@ public static final class Builder { /** * Constructs a {@code Builder}. * - * @param mapId the ID of the map to retrieve arrival information for + * @param mapId the ID of the map to retrieve arrivals for * @throws NullPointerException if {@code mapId} is {@code null} */ public Builder(String mapId) { @@ -79,7 +79,7 @@ public Builder(String mapId) { } /** - * Sets the train line to filter arrival information by. + * Sets the train line to filter arrivals by. * * @param line the train line * @return this {@code Builder} instance @@ -92,9 +92,9 @@ public Builder line(TrainLine line) { } /** - * Sets the maximum number of arrival information to return. + * Sets the maximum number of arrivals to return. * - * @param maxResults the maximum number of arrival information + * @param maxResults the maximum number of arrivals * @return this {@code Builder} instance * @throws IllegalArgumentException if {@code maxResults} is not positive */ diff --git a/src/main/java/com/cta4j/train/arrival/query/StopArrivalQuery.java b/src/main/java/com/cta4j/train/arrival/query/StopArrivalQuery.java index acc46194..ece9c3e5 100644 --- a/src/main/java/com/cta4j/train/arrival/query/StopArrivalQuery.java +++ b/src/main/java/com/cta4j/train/arrival/query/StopArrivalQuery.java @@ -7,11 +7,11 @@ import java.util.Objects; /** - * Represents a query for train arrival information for a specific stop. + * Represents a query for train arrivals at a specific stop. * - * @param stopId the ID of the stop to retrieve arrival information for - * @param line the optional train line to filter arrival information by - * @param maxResults the optional maximum number of arrival information to return + * @param stopId the ID of the stop to retrieve arrivals for + * @param line the optional train line to filter arrivals by + * @param maxResults the optional maximum number of arrivals to return */ @NullMarked public record StopArrivalQuery( @@ -22,9 +22,9 @@ public record StopArrivalQuery( /** * Constructs a {@code StopArrivalQuery}. * - * @param stopId the ID of the stop to retrieve arrival information for - * @param line the optional train line to filter arrival information by - * @param maxResults the optional maximum number of arrival information to return + * @param stopId the ID of the stop to retrieve arrivals for + * @param line the optional train line to filter arrivals by + * @param maxResults the optional maximum number of arrivals to return * @throws NullPointerException if {@code stopId} is {@code null} * @throws IllegalArgumentException if {@code maxResults} is non-{@code null} and not positive */ @@ -39,7 +39,7 @@ public record StopArrivalQuery( /** * Creates a builder for {@code StopArrivalQuery}. * - * @param stopId the ID of the stop to retrieve arrival information for + * @param stopId the ID of the stop to retrieve arrivals for * @return a new {@code Builder} instance * @throws NullPointerException if {@code stopId} is {@code null} */ @@ -52,18 +52,18 @@ public static Builder builder(String stopId) { */ public static final class Builder { /** - * The ID of the stop to retrieve arrival information for. + * The ID of the stop to retrieve arrivals for. */ private final String stopId; /** - * The optional train line to filter arrival information by. + * The optional train line to filter arrivals by. */ @Nullable private TrainLine line; /** - * The optional maximum number of arrival information to return. + * The optional maximum number of arrivals to return. */ @Nullable private Integer maxResults; @@ -71,7 +71,7 @@ public static final class Builder { /** * Constructs a {@code Builder}. * - * @param stopId the ID of the stop to retrieve arrival information for + * @param stopId the ID of the stop to retrieve arrivals for * @throws NullPointerException if {@code stopId} is {@code null} */ public Builder(String stopId) { @@ -79,7 +79,7 @@ public Builder(String stopId) { } /** - * Sets the train line to filter arrival information by. + * Sets the train line to filter arrivals by. * * @param line the train line * @return this {@code Builder} instance @@ -92,9 +92,9 @@ public Builder line(TrainLine line) { } /** - * Sets the maximum number of arrival information to return. + * Sets the maximum number of arrivals to return. * - * @param maxResults the maximum number of arrival information + * @param maxResults the maximum number of arrivals * @return this {@code Builder} instance * @throws IllegalArgumentException if {@code maxResults} is not positive */ From a378249f6fde1dd523047fe3a5916ed583946552 Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Sun, 19 Jul 2026 13:35:17 -0500 Subject: [PATCH 16/60] Refactor alert-related classes and update package structure for detailed alerts --- .../internal/wire/CtaCdata.java} | 6 +- .../detailedalert/DetailedAlertsApi.java | 7 ++ .../detailedalert/internal/wire/CtaAlert.java | 82 +++++++++++++++++++ .../internal/wire/CtaImpactedService.java | 32 ++++++++ .../internal/wire/CtaImpactedServices.java | 25 ++++++ .../query/AlertQuery.java | 4 +- .../internal/wire/CtaRouteInfo.java | 3 +- .../station/internal/wire/CtaLocation.java | 4 +- .../routestatus/RouteStatusMapperTest.java | 4 +- 9 files changed, 158 insertions(+), 9 deletions(-) rename src/main/java/com/cta4j/alert/{routestatus/internal/wire/CtaRouteInfoUrl.java => common/internal/wire/CtaCdata.java} (79%) create mode 100644 src/main/java/com/cta4j/alert/detailedalert/DetailedAlertsApi.java create mode 100644 src/main/java/com/cta4j/alert/detailedalert/internal/wire/CtaAlert.java create mode 100644 src/main/java/com/cta4j/alert/detailedalert/internal/wire/CtaImpactedService.java create mode 100644 src/main/java/com/cta4j/alert/detailedalert/internal/wire/CtaImpactedServices.java rename src/main/java/com/cta4j/alert/{detail => detailedalert}/query/AlertQuery.java (98%) diff --git a/src/main/java/com/cta4j/alert/routestatus/internal/wire/CtaRouteInfoUrl.java b/src/main/java/com/cta4j/alert/common/internal/wire/CtaCdata.java similarity index 79% rename from src/main/java/com/cta4j/alert/routestatus/internal/wire/CtaRouteInfoUrl.java rename to src/main/java/com/cta4j/alert/common/internal/wire/CtaCdata.java index 4d9f39d2..6e26e89f 100644 --- a/src/main/java/com/cta4j/alert/routestatus/internal/wire/CtaRouteInfoUrl.java +++ b/src/main/java/com/cta4j/alert/common/internal/wire/CtaCdata.java @@ -1,4 +1,4 @@ -package com.cta4j.alert.routestatus.internal.wire; +package com.cta4j.alert.common.internal.wire; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; @@ -10,10 +10,10 @@ @JsonIgnoreProperties(ignoreUnknown = true) @ApiStatus.Internal @NullMarked -public record CtaRouteInfoUrl( +public record CtaCdata( @JsonProperty("#cdata-section") String cdataSection ) { - public CtaRouteInfoUrl { + public CtaCdata { Objects.requireNonNull(cdataSection); } } diff --git a/src/main/java/com/cta4j/alert/detailedalert/DetailedAlertsApi.java b/src/main/java/com/cta4j/alert/detailedalert/DetailedAlertsApi.java new file mode 100644 index 00000000..184f7f4e --- /dev/null +++ b/src/main/java/com/cta4j/alert/detailedalert/DetailedAlertsApi.java @@ -0,0 +1,7 @@ +package com.cta4j.alert.detailedalert; + +import org.jspecify.annotations.NullMarked; + +@NullMarked +public interface DetailedAlertsApi { +} diff --git a/src/main/java/com/cta4j/alert/detailedalert/internal/wire/CtaAlert.java b/src/main/java/com/cta4j/alert/detailedalert/internal/wire/CtaAlert.java new file mode 100644 index 00000000..7fcabad2 --- /dev/null +++ b/src/main/java/com/cta4j/alert/detailedalert/internal/wire/CtaAlert.java @@ -0,0 +1,82 @@ +package com.cta4j.alert.detailedalert.internal.wire; + +import com.cta4j.alert.common.internal.wire.CtaCdata; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +import java.util.Objects; + +@JsonIgnoreProperties(ignoreUnknown = true) +@ApiStatus.Internal +@NullMarked +public record CtaAlert( + @JsonProperty("AlertId") + String alertId, + + @JsonProperty("Headline") + String headline, + + @JsonProperty("ShortDescription") + String shortDescription, + + @JsonProperty("FullDescription") + CtaCdata fullDescription, + + @JsonProperty("SeverityScore") + String severityScore, + + @JsonProperty("SeverityColor") + String severityColor, + + @JsonProperty("SeverityCSS") + String severityCss, + + @JsonProperty("Impact") + String impact, + + @JsonProperty("EventStart") + String eventStart, + + @JsonProperty("EventEnd") + @Nullable + String eventEnd, + + @JsonProperty("TBD") + String tbd, + + @JsonProperty("MajorAlert") + String majorAlert, + + @JsonProperty("AlertURL") + CtaCdata alertUrl, + + @JsonProperty("ImpactedService") + CtaImpactedServices impactedService, + + @JsonProperty("ttim") + String ttim, + + @JsonProperty("GUID") + String guid +) { + public CtaAlert { + Objects.requireNonNull(alertId); + Objects.requireNonNull(headline); + Objects.requireNonNull(shortDescription); + Objects.requireNonNull(fullDescription); + Objects.requireNonNull(severityScore); + Objects.requireNonNull(severityColor); + Objects.requireNonNull(severityCss); + Objects.requireNonNull(impact); + Objects.requireNonNull(eventStart); + Objects.requireNonNull(tbd); + Objects.requireNonNull(majorAlert); + Objects.requireNonNull(alertUrl); + Objects.requireNonNull(impactedService); + Objects.requireNonNull(ttim); + Objects.requireNonNull(guid); + } +} diff --git a/src/main/java/com/cta4j/alert/detailedalert/internal/wire/CtaImpactedService.java b/src/main/java/com/cta4j/alert/detailedalert/internal/wire/CtaImpactedService.java new file mode 100644 index 00000000..2038eb3c --- /dev/null +++ b/src/main/java/com/cta4j/alert/detailedalert/internal/wire/CtaImpactedService.java @@ -0,0 +1,32 @@ +package com.cta4j.alert.detailedalert.internal.wire; + +import com.cta4j.alert.common.internal.wire.CtaCdata; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; + +import java.util.Objects; + +@JsonIgnoreProperties(ignoreUnknown = true) +@ApiStatus.Internal +@NullMarked +public record CtaImpactedService( + @JsonProperty("ServiceType") String serviceType, + @JsonProperty("ServiceTypeDescription") String serviceTypeDescription, + @JsonProperty("ServiceName") String serviceName, + @JsonProperty("ServiceId") String serviceId, + @JsonProperty("ServiceBackColor") String serviceBackColor, + @JsonProperty("ServiceTextColor") String serviceTextColor, + @JsonProperty("ServiceURL") CtaCdata serviceUrl +) { + public CtaImpactedService { + Objects.requireNonNull(serviceType); + Objects.requireNonNull(serviceTypeDescription); + Objects.requireNonNull(serviceName); + Objects.requireNonNull(serviceId); + Objects.requireNonNull(serviceBackColor); + Objects.requireNonNull(serviceTextColor); + Objects.requireNonNull(serviceUrl); + } +} diff --git a/src/main/java/com/cta4j/alert/detailedalert/internal/wire/CtaImpactedServices.java b/src/main/java/com/cta4j/alert/detailedalert/internal/wire/CtaImpactedServices.java new file mode 100644 index 00000000..c01ea58d --- /dev/null +++ b/src/main/java/com/cta4j/alert/detailedalert/internal/wire/CtaImpactedServices.java @@ -0,0 +1,25 @@ +package com.cta4j.alert.detailedalert.internal.wire; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; + +import java.util.List; +import java.util.Objects; + +@JsonIgnoreProperties(ignoreUnknown = true) +@ApiStatus.Internal +@NullMarked +public record CtaImpactedServices( + @JsonProperty("Service") + @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY) + List service +) { + public CtaImpactedServices { + Objects.requireNonNull(service); + + service = List.copyOf(service); + } +} diff --git a/src/main/java/com/cta4j/alert/detail/query/AlertQuery.java b/src/main/java/com/cta4j/alert/detailedalert/query/AlertQuery.java similarity index 98% rename from src/main/java/com/cta4j/alert/detail/query/AlertQuery.java rename to src/main/java/com/cta4j/alert/detailedalert/query/AlertQuery.java index 214cbe30..82a9d1ed 100644 --- a/src/main/java/com/cta4j/alert/detail/query/AlertQuery.java +++ b/src/main/java/com/cta4j/alert/detailedalert/query/AlertQuery.java @@ -1,4 +1,4 @@ -package com.cta4j.alert.detail.query; +package com.cta4j.alert.detailedalert.query; import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; @@ -7,7 +7,7 @@ import java.util.Objects; /** - * Represents a query for detailed alert information. + * Represents a query for detailed alerts. * * @param activeOnly whether to include only alerts that are currently active * @param accessibility whether to include alerts that affect accessible paths in stations diff --git a/src/main/java/com/cta4j/alert/routestatus/internal/wire/CtaRouteInfo.java b/src/main/java/com/cta4j/alert/routestatus/internal/wire/CtaRouteInfo.java index a9f63512..3b298cf0 100644 --- a/src/main/java/com/cta4j/alert/routestatus/internal/wire/CtaRouteInfo.java +++ b/src/main/java/com/cta4j/alert/routestatus/internal/wire/CtaRouteInfo.java @@ -1,5 +1,6 @@ package com.cta4j.alert.routestatus.internal.wire; +import com.cta4j.alert.common.internal.wire.CtaCdata; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import org.jetbrains.annotations.ApiStatus; @@ -15,7 +16,7 @@ public record CtaRouteInfo( @JsonProperty("RouteColorCode") String routeColorCode, @JsonProperty("RouteTextColor") String routeTextColor, @JsonProperty("ServiceId") String serviceId, - @JsonProperty("RouteURL") CtaRouteInfoUrl routeUrl, + @JsonProperty("RouteURL") CtaCdata routeUrl, @JsonProperty("RouteStatus") String routeStatus, @JsonProperty("RouteStatusColor") String routeStatusColor ) { diff --git a/src/main/java/com/cta4j/train/station/internal/wire/CtaLocation.java b/src/main/java/com/cta4j/train/station/internal/wire/CtaLocation.java index 316ae6c5..9420e76b 100644 --- a/src/main/java/com/cta4j/train/station/internal/wire/CtaLocation.java +++ b/src/main/java/com/cta4j/train/station/internal/wire/CtaLocation.java @@ -13,9 +13,11 @@ @NullMarked public record CtaLocation( String latitude, + String longitude, - @Nullable + @JsonProperty("human_address") + @Nullable String humanAddress ) { public CtaLocation { diff --git a/src/test/java/com/cta4j/alert/routestatus/RouteStatusMapperTest.java b/src/test/java/com/cta4j/alert/routestatus/RouteStatusMapperTest.java index 38685de1..56bc597a 100644 --- a/src/test/java/com/cta4j/alert/routestatus/RouteStatusMapperTest.java +++ b/src/test/java/com/cta4j/alert/routestatus/RouteStatusMapperTest.java @@ -2,7 +2,7 @@ import com.cta4j.alert.routestatus.internal.mapper.RouteStatusMapper; import com.cta4j.alert.routestatus.internal.wire.CtaRouteInfo; -import com.cta4j.alert.routestatus.internal.wire.CtaRouteInfoUrl; +import com.cta4j.alert.common.internal.wire.CtaCdata; import com.cta4j.alert.routestatus.model.RouteStatus; import org.junit.jupiter.api.Test; @@ -13,7 +13,7 @@ class RouteStatusMapperTest { void toDomain_mapsAllFields() { CtaRouteInfo wire = new CtaRouteInfo( "Clark", "565a5c", "ffffff", "22", - new CtaRouteInfoUrl("http://www.transitchicago.com/bus/22/"), + new CtaCdata("http://www.transitchicago.com/bus/22/"), "Bus Stop Note", "000000" ); From 2f2003aa71675a2ea267898f41402201c2348406 Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Sun, 19 Jul 2026 14:52:39 -0500 Subject: [PATCH 17/60] Add detailed alert model classes and response handling --- .../internal/wire/CtaAlerts.java | 38 ++++++++++++++++++ .../wire/CtaDetailedAlertsResponse.java | 19 +++++++++ .../alert/detailedalert/model/Alert.java | 39 +++++++++++++++++++ .../detailedalert/model/ImpactedService.java | 26 +++++++++++++ .../alert/detailedalert/model/Severity.java | 21 ++++++++++ 5 files changed, 143 insertions(+) create mode 100644 src/main/java/com/cta4j/alert/detailedalert/internal/wire/CtaAlerts.java create mode 100644 src/main/java/com/cta4j/alert/detailedalert/internal/wire/CtaDetailedAlertsResponse.java create mode 100644 src/main/java/com/cta4j/alert/detailedalert/model/Alert.java create mode 100644 src/main/java/com/cta4j/alert/detailedalert/model/ImpactedService.java create mode 100644 src/main/java/com/cta4j/alert/detailedalert/model/Severity.java diff --git a/src/main/java/com/cta4j/alert/detailedalert/internal/wire/CtaAlerts.java b/src/main/java/com/cta4j/alert/detailedalert/internal/wire/CtaAlerts.java new file mode 100644 index 00000000..f7285509 --- /dev/null +++ b/src/main/java/com/cta4j/alert/detailedalert/internal/wire/CtaAlerts.java @@ -0,0 +1,38 @@ +package com.cta4j.alert.detailedalert.internal.wire; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +import java.util.List; +import java.util.Objects; + +@JsonIgnoreProperties(ignoreUnknown = true) +@ApiStatus.Internal +@NullMarked +public record CtaAlerts( + @JsonProperty("TimeStamp") + String timestamp, + + @JsonProperty("ErrorCode") + String errorCode, + + @JsonProperty("ErrorMessage") + @Nullable + String errorMessage, + + @JsonProperty("Alert") + @Nullable + List alert +) { + public CtaAlerts { + Objects.requireNonNull(timestamp); + Objects.requireNonNull(errorCode); + + if (alert != null) { + alert = List.copyOf(alert); + } + } +} diff --git a/src/main/java/com/cta4j/alert/detailedalert/internal/wire/CtaDetailedAlertsResponse.java b/src/main/java/com/cta4j/alert/detailedalert/internal/wire/CtaDetailedAlertsResponse.java new file mode 100644 index 00000000..369012d4 --- /dev/null +++ b/src/main/java/com/cta4j/alert/detailedalert/internal/wire/CtaDetailedAlertsResponse.java @@ -0,0 +1,19 @@ +package com.cta4j.alert.detailedalert.internal.wire; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; + +import java.util.Objects; + +@JsonIgnoreProperties(ignoreUnknown = true) +@ApiStatus.Internal +@NullMarked +public record CtaDetailedAlertsResponse( + @JsonProperty("CTAAlerts") CtaAlerts ctaAlerts +) { + public CtaDetailedAlertsResponse { + Objects.requireNonNull(ctaAlerts); + } +} diff --git a/src/main/java/com/cta4j/alert/detailedalert/model/Alert.java b/src/main/java/com/cta4j/alert/detailedalert/model/Alert.java new file mode 100644 index 00000000..e2092c61 --- /dev/null +++ b/src/main/java/com/cta4j/alert/detailedalert/model/Alert.java @@ -0,0 +1,39 @@ +package com.cta4j.alert.detailedalert.model; + +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +import java.net.URI; +import java.time.Instant; +import java.util.List; +import java.util.Objects; + +@NullMarked +public record Alert( + String id, + String headline, + String shortDescription, + String fullDescription, + Severity severity, + String impact, + Instant startTime, + @Nullable Instant endTime, + boolean openEnded, + boolean major, + URI url, + List impactedServices +) { + public Alert { + Objects.requireNonNull(id); + Objects.requireNonNull(headline); + Objects.requireNonNull(shortDescription); + Objects.requireNonNull(fullDescription); + Objects.requireNonNull(severity); + Objects.requireNonNull(impact); + Objects.requireNonNull(startTime); + Objects.requireNonNull(url); + Objects.requireNonNull(impactedServices); + + impactedServices = List.copyOf(impactedServices); + } +} diff --git a/src/main/java/com/cta4j/alert/detailedalert/model/ImpactedService.java b/src/main/java/com/cta4j/alert/detailedalert/model/ImpactedService.java new file mode 100644 index 00000000..21707c7f --- /dev/null +++ b/src/main/java/com/cta4j/alert/detailedalert/model/ImpactedService.java @@ -0,0 +1,26 @@ +package com.cta4j.alert.detailedalert.model; + +import com.cta4j.alert.common.model.ServiceType; +import org.jspecify.annotations.NullMarked; + +import java.net.URI; +import java.util.Objects; + +@NullMarked +public record ImpactedService( + ServiceType type, + String name, + String serviceId, + String color, + String textColor, + URI url +) { + public ImpactedService { + Objects.requireNonNull(type); + Objects.requireNonNull(name); + Objects.requireNonNull(serviceId); + Objects.requireNonNull(color); + Objects.requireNonNull(textColor); + Objects.requireNonNull(url); + } +} diff --git a/src/main/java/com/cta4j/alert/detailedalert/model/Severity.java b/src/main/java/com/cta4j/alert/detailedalert/model/Severity.java new file mode 100644 index 00000000..4affeff8 --- /dev/null +++ b/src/main/java/com/cta4j/alert/detailedalert/model/Severity.java @@ -0,0 +1,21 @@ +package com.cta4j.alert.detailedalert.model; + +import org.jspecify.annotations.NullMarked; + +import java.util.Objects; + +@NullMarked +public record Severity( + int score, + String color, + String css +) { + public Severity { + Objects.requireNonNull(color); + Objects.requireNonNull(css); + + if (score < 0 || score > 99) { + throw new IllegalArgumentException("score must be between 0 and 99 (inclusive)"); + } + } +} From 3d7a7f4f5309285d8d6ce5cd5dab3bc5b8a60db9 Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Sun, 19 Jul 2026 15:11:26 -0500 Subject: [PATCH 18/60] Add detailed documentation for Alert, ImpactedService, and Severity classes --- .../alert/detailedalert/model/Alert.java | 37 +++++++++++++++++++ .../detailedalert/model/ImpactedService.java | 25 +++++++++++++ .../alert/detailedalert/model/Severity.java | 23 ++++++++++++ 3 files changed, 85 insertions(+) diff --git a/src/main/java/com/cta4j/alert/detailedalert/model/Alert.java b/src/main/java/com/cta4j/alert/detailedalert/model/Alert.java index e2092c61..30f12797 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/model/Alert.java +++ b/src/main/java/com/cta4j/alert/detailedalert/model/Alert.java @@ -8,6 +8,23 @@ import java.util.List; import java.util.Objects; +/** + * Represents a detailed alert describing an event that affects one or more CTA services. + * + * @param id the unique ID of this alert + * @param headline the headline of this alert + * @param shortDescription the short description of this alert + * @param fullDescription the full description of this alert + * @param severity the severity of this alert + * @param impact the descriptive text of the impact this alert has on service (e.g., "Elevator Status", + * "Bus Stop Relocation", "Planned Reroute") + * @param startTime the start time of this alert + * @param endTime the end time of this alert, or {@code null} if not known + * @param openEnded whether this alert is open-ended (has no known end time) + * @param major whether this alert is of major significance + * @param url the URL of this alert's detail page on transitchicago.com + * @param impactedServices the services impacted by this alert + */ @NullMarked public record Alert( String id, @@ -23,6 +40,26 @@ public record Alert( URI url, List impactedServices ) { + /** + * Constructs an {@code Alert}. + * + * @param id the unique ID of the alert + * @param headline the headline of the alert + * @param shortDescription the short description of the alert + * @param fullDescription the full description of the alert + * @param severity the severity of the alert + * @param impact the descriptive text of the impact the alert has on service (e.g., "Elevator Status", + * "Bus Stop Relocation", "Planned Reroute") + * @param startTime the start time of the alert + * @param endTime the end time of the alert, or {@code null} if not known + * @param openEnded whether the alert is open-ended (has no known end time) + * @param major whether the alert is of major significance + * @param url the URL of the alert's detail page on transitchicago.com + * @param impactedServices the services impacted by the alert + * @throws NullPointerException if {@code id}, {@code headline}, {@code shortDescription}, + * {@code fullDescription}, {@code severity}, {@code impact}, {@code startTime}, {@code url}, or + * {@code impactedServices} is {@code null}, or if any element of {@code impactedServices} is {@code null} + */ public Alert { Objects.requireNonNull(id); Objects.requireNonNull(headline); diff --git a/src/main/java/com/cta4j/alert/detailedalert/model/ImpactedService.java b/src/main/java/com/cta4j/alert/detailedalert/model/ImpactedService.java index 21707c7f..d629f0b2 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/model/ImpactedService.java +++ b/src/main/java/com/cta4j/alert/detailedalert/model/ImpactedService.java @@ -6,6 +6,18 @@ import java.net.URI; import java.util.Objects; +/** + * Represents a single service - a bus route, train route, train station, or systemwide grouping - impacted by + * an alert. + * + * @param type the type of service this represents + * @param name the name of this service (e.g., "Clark", "Red Line", "Jackson", "All Bus Routes") + * @param serviceId the identifier of this service; matches GTFS route or station IDs, except for systemwide groupings, + * which use a fixed identifier instead (e.g., "22", "Red", "Systemwide") + * @param color the color of this service used in maps, as {@code rrggbb} + * @param textColor the suggested color of text displayed against {@code color}, as {@code rrggbb} + * @param url the URL of this service's page on transitchicago.com + */ @NullMarked public record ImpactedService( ServiceType type, @@ -15,6 +27,19 @@ public record ImpactedService( String textColor, URI url ) { + /** + * Constructs an {@code ImpactedService}. + * + * @param type the type of service the impacted service represents + * @param name the name of the service (e.g., "Clark", "Red Line", "Jackson", "All Bus Routes") + * @param serviceId the identifier of the service; matches GTFS route or station IDs, except for systemwide + * groupings, which use a fixed identifier instead (e.g., "22", "Red", "Systemwide") + * @param color the color of the service used in maps, as {@code rrggbb} + * @param textColor the suggested color of text displayed against {@code color}, as {@code rrggbb} + * @param url the URL of the service's page on transitchicago.com + * @throws NullPointerException if {@code type}, {@code name}, {@code serviceId}, {@code color}, + * {@code textColor}, or {@code url} is {@code null} + */ public ImpactedService { Objects.requireNonNull(type); Objects.requireNonNull(name); diff --git a/src/main/java/com/cta4j/alert/detailedalert/model/Severity.java b/src/main/java/com/cta4j/alert/detailedalert/model/Severity.java index 4affeff8..d9a258c3 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/model/Severity.java +++ b/src/main/java/com/cta4j/alert/detailedalert/model/Severity.java @@ -4,12 +4,35 @@ import java.util.Objects; +/** + * Represents the severity of an alert. + * + * @param score the numerical score used to rank this severity, based on the alert's impact on overall service, between + * 0 and 99 (inclusive) + * @param color the hexadecimal RGB color code used to color this severity's text on transitchicago.com, as + * {@code rrggbb} + * @param css the category used to pick the icon and display style of the alert (e.g., "normal", "planned", "minor", + * "major"); note that this set is not exhaustive, as other values (e.g., "special-note") have been observed + * in practice that are not documented in the CTA Alerts API documentation + */ @NullMarked public record Severity( int score, String color, String css ) { + /** + * Constructs a {@code Severity}. + * + * @param score the numerical score used to rank the severity, based on the alert's impact on overall + * service, between 0 and 99 (inclusive) + * @param color the hexadecimal RGB color code used to color the severity's text on transitchicago.com, as + * {@code rrggbb} + * @param css the category used to pick the icon and display style of the alert (e.g., "normal", "planned", + * "minor", "major") + * @throws NullPointerException if {@code color} or {@code css} is {@code null} + * @throws IllegalArgumentException if {@code score} is not between 0 and 99 (inclusive) + */ public Severity { Objects.requireNonNull(color); Objects.requireNonNull(css); From f1bbf76b1f06f1b804cf91016c997e0ab35ddcfa Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Sun, 19 Jul 2026 16:13:55 -0500 Subject: [PATCH 19/60] Update Javadoc comments for clarity and consistency across various classes --- .../cta4j/alert/detailedalert/model/Alert.java | 4 ++-- .../detailedalert/model/ImpactedService.java | 10 ++++++---- .../alert/detailedalert/model/Severity.java | 17 ++++++++--------- .../cta4j/alert/routestatus/RouteStatusApi.java | 6 +++--- .../alert/routestatus/model/RouteStatus.java | 16 ++++++++++------ .../java/com/cta4j/bus/detour/DetoursApi.java | 2 +- .../bus/detour/model/DetourRouteDirection.java | 4 ++-- .../com/cta4j/bus/direction/DirectionsApi.java | 2 +- .../cta4j/bus/locale/model/SupportedLocale.java | 4 ++-- .../cta4j/bus/prediction/model/Prediction.java | 14 ++++++++------ .../java/com/cta4j/bus/route/model/Route.java | 6 ++++-- .../java/com/cta4j/bus/stop/model/Stop.java | 4 ++-- .../com/cta4j/bus/vehicle/model/Vehicle.java | 4 ++-- .../train/common/model/ArrivalMetadata.java | 5 +++++ .../train/location/model/LocationTrain.java | 5 +++++ 15 files changed, 61 insertions(+), 42 deletions(-) diff --git a/src/main/java/com/cta4j/alert/detailedalert/model/Alert.java b/src/main/java/com/cta4j/alert/detailedalert/model/Alert.java index 30f12797..0be7bdf2 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/model/Alert.java +++ b/src/main/java/com/cta4j/alert/detailedalert/model/Alert.java @@ -11,7 +11,7 @@ /** * Represents a detailed alert describing an event that affects one or more CTA services. * - * @param id the unique ID of this alert + * @param id the unique ID of this alert (e.g., "115070") * @param headline the headline of this alert * @param shortDescription the short description of this alert * @param fullDescription the full description of this alert @@ -43,7 +43,7 @@ public record Alert( /** * Constructs an {@code Alert}. * - * @param id the unique ID of the alert + * @param id the unique ID of the alert (e.g., "115070") * @param headline the headline of the alert * @param shortDescription the short description of the alert * @param fullDescription the full description of the alert diff --git a/src/main/java/com/cta4j/alert/detailedalert/model/ImpactedService.java b/src/main/java/com/cta4j/alert/detailedalert/model/ImpactedService.java index d629f0b2..2e3bb456 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/model/ImpactedService.java +++ b/src/main/java/com/cta4j/alert/detailedalert/model/ImpactedService.java @@ -14,8 +14,9 @@ * @param name the name of this service (e.g., "Clark", "Red Line", "Jackson", "All Bus Routes") * @param serviceId the identifier of this service; matches GTFS route or station IDs, except for systemwide groupings, * which use a fixed identifier instead (e.g., "22", "Red", "Systemwide") - * @param color the color of this service used in maps, as {@code rrggbb} - * @param textColor the suggested color of text displayed against {@code color}, as {@code rrggbb} + * @param color the color of this service used in maps; length and casing vary (e.g., "059", "565a5c") + * @param textColor the suggested color of text displayed against {@code color}; casing varies (e.g., "ffffff", + * "FFFFFF") * @param url the URL of this service's page on transitchicago.com */ @NullMarked @@ -34,8 +35,9 @@ public record ImpactedService( * @param name the name of the service (e.g., "Clark", "Red Line", "Jackson", "All Bus Routes") * @param serviceId the identifier of the service; matches GTFS route or station IDs, except for systemwide * groupings, which use a fixed identifier instead (e.g., "22", "Red", "Systemwide") - * @param color the color of the service used in maps, as {@code rrggbb} - * @param textColor the suggested color of text displayed against {@code color}, as {@code rrggbb} + * @param color the color of the service used in maps; length and casing vary (e.g., "059", "565a5c") + * @param textColor the suggested color of text displayed against {@code color}; casing varies (e.g., "ffffff", + * "FFFFFF") * @param url the URL of the service's page on transitchicago.com * @throws NullPointerException if {@code type}, {@code name}, {@code serviceId}, {@code color}, * {@code textColor}, or {@code url} is {@code null} diff --git a/src/main/java/com/cta4j/alert/detailedalert/model/Severity.java b/src/main/java/com/cta4j/alert/detailedalert/model/Severity.java index d9a258c3..dd3c3ae9 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/model/Severity.java +++ b/src/main/java/com/cta4j/alert/detailedalert/model/Severity.java @@ -9,11 +9,10 @@ * * @param score the numerical score used to rank this severity, based on the alert's impact on overall service, between * 0 and 99 (inclusive) - * @param color the hexadecimal RGB color code used to color this severity's text on transitchicago.com, as - * {@code rrggbb} - * @param css the category used to pick the icon and display style of the alert (e.g., "normal", "planned", "minor", - * "major"); note that this set is not exhaustive, as other values (e.g., "special-note") have been observed - * in practice that are not documented in the CTA Alerts API documentation + * @param color the hexadecimal RGB color code used to color this severity's text on transitchicago.com; length and + * casing vary (e.g., "000000", "06c", "B45F04") + * @param css the category used to pick the icon and display style of the alert; not limited to the four documented + * values (e.g., "normal", "planned", "minor", "major", "special-note") */ @NullMarked public record Severity( @@ -26,10 +25,10 @@ public record Severity( * * @param score the numerical score used to rank the severity, based on the alert's impact on overall * service, between 0 and 99 (inclusive) - * @param color the hexadecimal RGB color code used to color the severity's text on transitchicago.com, as - * {@code rrggbb} - * @param css the category used to pick the icon and display style of the alert (e.g., "normal", "planned", - * "minor", "major") + * @param color the hexadecimal RGB color code used to color the severity's text on transitchicago.com; length and + * casing vary (e.g., "000000", "06c", "B45F04") + * @param css the category used to pick the icon and display style of the alert; not limited to the four + * documented values (e.g., "normal", "planned", "minor", "major", "special-note") * @throws NullPointerException if {@code color} or {@code css} is {@code null} * @throws IllegalArgumentException if {@code score} is not between 0 and 99 (inclusive) */ diff --git a/src/main/java/com/cta4j/alert/routestatus/RouteStatusApi.java b/src/main/java/com/cta4j/alert/routestatus/RouteStatusApi.java index b21838c4..30787666 100644 --- a/src/main/java/com/cta4j/alert/routestatus/RouteStatusApi.java +++ b/src/main/java/com/cta4j/alert/routestatus/RouteStatusApi.java @@ -61,8 +61,8 @@ default List findByType(ServiceType type) { * @return a {@link List} of {@link RouteStatus}es associated with the bus route IDs, or an empty {@link List} if * no route statuses are found for the bus route IDs * @throws NullPointerException if {@code routeIds} is {@code null} or contains {@code null} elements - * @throws IllegalArgumentException if any of the {@code routeIds} matches a train line code (e.g. {@code "Red"}); - * use {@link #findByLines(Collection)} instead + * @throws IllegalArgumentException if any of the {@code routeIds} matches a train line code (e.g., "Red"); use + * {@link #findByLines(Collection)} instead * @throws Cta4jRouteStatusException if the API returns an error response or the response cannot be parsed */ List findByBusRouteIds(Collection routeIds); @@ -74,7 +74,7 @@ default List findByType(ServiceType type) { * @return a {@link List} of {@link RouteStatus}es associated with the bus route ID, or an empty {@link List} if * no route statuses are found for the bus route ID * @throws NullPointerException if {@code routeId} is {@code null} - * @throws IllegalArgumentException if {@code routeId} matches a train line code (e.g. {@code "Red"}); use + * @throws IllegalArgumentException if {@code routeId} matches a train line code (e.g., "Red"); use * {@link #findByLine(TrainLine)} instead * @throws Cta4jRouteStatusException if the API returns an error response or the response cannot be parsed */ diff --git a/src/main/java/com/cta4j/alert/routestatus/model/RouteStatus.java b/src/main/java/com/cta4j/alert/routestatus/model/RouteStatus.java index 701ae737..73560215 100644 --- a/src/main/java/com/cta4j/alert/routestatus/model/RouteStatus.java +++ b/src/main/java/com/cta4j/alert/routestatus/model/RouteStatus.java @@ -9,13 +9,15 @@ * Represents the service status of a single route. * * @param route the name of this route (e.g., "Clark") - * @param color the color of this route used in maps, as {@code rrggbb} (e.g., "565a5c") - * @param textColor the suggested color of text displayed against {@code color}, as {@code rrggbb} (e.g., "ffffff") + * @param color the color of this route used in maps; casing varies (e.g., "565a5c", "0065BD") + * @param textColor the suggested color of text displayed against {@code color}; casing varies (e.g., "ffffff", + * "FFFFFF") * @param serviceId the unique GTFS route or station identifier of this route (e.g., "22") * @param url the URL of this route's or station's page on transitchicago.com * @param status the ultimate, human-readable status of this route (e.g., "Normal Service", "Service Change", * "Bus Stop Note") - * @param statusColor the suggested color associated with {@code status}, as {@code rrggbb} (e.g., "000000") + * @param statusColor the suggested color associated with {@code status}; length and casing vary (e.g., "000000", + * "06c", "B45F04") */ @NullMarked public record RouteStatus( @@ -31,13 +33,15 @@ public record RouteStatus( * Constructs a {@code RouteStatus}. * * @param route the name of the route (e.g., "Clark") - * @param color the color of the route used in maps, as {@code rrggbb} (e.g., "565a5c") - * @param textColor the suggested color of text displayed against {@code color}, as {@code rrggbb} (e.g., "ffffff") + * @param color the color of the route used in maps; casing varies (e.g., "565a5c", "0065BD") + * @param textColor the suggested color of text displayed against {@code color}; casing varies (e.g., "ffffff", + * "FFFFFF") * @param serviceId the unique GTFS route or station identifier of the route (e.g., "22") * @param url the URL of the route's or station's page on transitchicago.com * @param status the ultimate, human-readable status of the route (e.g., "Normal Service", "Service Change", * "Bus Stop Note") - * @param statusColor the suggested color associated with {@code status}, as {@code rrggbb} (e.g., "000000") + * @param statusColor the suggested color associated with {@code status}; length and casing vary (e.g., "000000", + * "06c", "B45F04") * @throws NullPointerException if {@code route}, {@code color}, {@code textColor}, {@code serviceId}, * {@code url}, {@code status}, or {@code statusColor} is {@code null} */ diff --git a/src/main/java/com/cta4j/bus/detour/DetoursApi.java b/src/main/java/com/cta4j/bus/detour/DetoursApi.java index 1583e713..771dde95 100644 --- a/src/main/java/com/cta4j/bus/detour/DetoursApi.java +++ b/src/main/java/com/cta4j/bus/detour/DetoursApi.java @@ -37,7 +37,7 @@ public interface DetoursApi { * Retrieves all active detours for the specified route ID and direction. * * @param routeId the route ID - * @param direction the travel direction (e.g. Northbound, Southbound) + * @param direction the travel direction (e.g., "Northbound", "Southbound") * @return a {@link List} of {@link Detour}s associated with the route ID and direction, or an empty {@link List} * if no detours are found for the route ID and direction * @throws NullPointerException if {@code routeId} or {@code direction} is {@code null} diff --git a/src/main/java/com/cta4j/bus/detour/model/DetourRouteDirection.java b/src/main/java/com/cta4j/bus/detour/model/DetourRouteDirection.java index 9ebfca09..74b36915 100644 --- a/src/main/java/com/cta4j/bus/detour/model/DetourRouteDirection.java +++ b/src/main/java/com/cta4j/bus/detour/model/DetourRouteDirection.java @@ -8,7 +8,7 @@ * Represents a route and direction affected by a detour. * * @param routeId the route ID of this detour - * @param direction the direction of this detour + * @param direction the direction of this detour (e.g., "Northbound", "Southbound") */ @NullMarked public record DetourRouteDirection( @@ -19,7 +19,7 @@ public record DetourRouteDirection( * Constructs a {@code DetourRouteDirection}. * * @param routeId the route ID of the detour - * @param direction the direction of the detour + * @param direction the direction of the detour (e.g., "Northbound", "Southbound") * @throws NullPointerException if {@code routeId} or {@code direction} is {@code null} */ public DetourRouteDirection { diff --git a/src/main/java/com/cta4j/bus/direction/DirectionsApi.java b/src/main/java/com/cta4j/bus/direction/DirectionsApi.java index 065f00da..575edd72 100644 --- a/src/main/java/com/cta4j/bus/direction/DirectionsApi.java +++ b/src/main/java/com/cta4j/bus/direction/DirectionsApi.java @@ -13,7 +13,7 @@ @NullMarked public interface DirectionsApi { /** - * Retrieves the available travel directions for the specified route (e.g., Northbound, Southbound). + * Retrieves the available travel directions for the specified route (e.g., "Northbound", "Southbound"). * * @param routeId the route identifier * @return a {@link List} of direction identifiers for the route, or an empty {@link List} if no directions are diff --git a/src/main/java/com/cta4j/bus/locale/model/SupportedLocale.java b/src/main/java/com/cta4j/bus/locale/model/SupportedLocale.java index 435df024..59ba0f71 100644 --- a/src/main/java/com/cta4j/bus/locale/model/SupportedLocale.java +++ b/src/main/java/com/cta4j/bus/locale/model/SupportedLocale.java @@ -9,7 +9,7 @@ * Represents a locale supported by the CTA Bus API. * * @param locale the supported {@link Locale} - * @param displayName the human-readable name of this supported locale + * @param displayName the human-readable name of this supported locale (e.g., "English", "Spanish") */ @NullMarked public record SupportedLocale( @@ -20,7 +20,7 @@ public record SupportedLocale( * Constructs a {@code SupportedLocale}. * * @param locale the supported {@link Locale} - * @param displayName the human-readable name of the supported locale + * @param displayName the human-readable name of the supported locale (e.g., "English", "Spanish") * @throws NullPointerException if {@code locale} or {@code displayName} is {@code null} */ public SupportedLocale { diff --git a/src/main/java/com/cta4j/bus/prediction/model/Prediction.java b/src/main/java/com/cta4j/bus/prediction/model/Prediction.java index 107c8774..ad69adbc 100644 --- a/src/main/java/com/cta4j/bus/prediction/model/Prediction.java +++ b/src/main/java/com/cta4j/bus/prediction/model/Prediction.java @@ -17,9 +17,10 @@ * @param vehicleId the unique identifier of the vehicle for which this prediction was generated * @param distanceToStop the feet left to be traveled by the vehicle before it reaches the stop associated with this * prediction - * @param routeId the alphanumeric designator of the route (e.g. "20" or "X20") for which this prediction was generated - * @param routeDesignator the language-specific route designator of this prediction, intended for display - * @param routeDirection the direction of travel of the route associated with this prediction (e.g. "Eastbound") + * @param routeId the alphanumeric designator of the route (e.g., "20" or "X9") for which this prediction was generated + * @param routeDesignator the language-specific route designator of this prediction, intended for display; identical + * to {@code routeId} in practice (e.g., "20") + * @param routeDirection the direction of travel of the route associated with this prediction (e.g., "Eastbound") * @param destination the final destination of the vehicle associated with this prediction * @param arrivalTime the predicted date and time (UTC) of a vehicle’s arrival or departure to the stop associated with * this prediction @@ -50,10 +51,11 @@ public record Prediction( * @param vehicleId the unique identifier of the vehicle for which the prediction was generated * @param distanceToStop the feet left to be traveled by the vehicle before it reaches the stop associated with the * prediction - * @param routeId the alphanumeric designator of the route (e.g. "20" or "X20") for which the prediction was + * @param routeId the alphanumeric designator of the route (e.g., "20" or "X9") for which the prediction was * generated - * @param routeDesignator the language-specific route designator of the prediction, intended for display - * @param routeDirection the direction of travel of the route associated with the prediction (e.g. "Eastbound") + * @param routeDesignator the language-specific route designator of the prediction, intended for display; + * identical to {@code routeId} in practice (e.g., "20") + * @param routeDirection the direction of travel of the route associated with the prediction (e.g., "Eastbound") * @param destination the final destination of the vehicle associated with the prediction * @param arrivalTime the predicted date and time (UTC) of a vehicle’s arrival or departure to the stop associated * with the prediction diff --git a/src/main/java/com/cta4j/bus/route/model/Route.java b/src/main/java/com/cta4j/bus/route/model/Route.java index 61000939..91cc3cdd 100644 --- a/src/main/java/com/cta4j/bus/route/model/Route.java +++ b/src/main/java/com/cta4j/bus/route/model/Route.java @@ -16,7 +16,8 @@ * @param id the alphanumeric designator of this route (e.g., "22", "J14", "X9") * @param name the common name of this route (e.g., "Clark", "Jeffery Jump", "Ashland Express") * @param color the color of this route used in maps (e.g., "#ffffff") - * @param designator the language-specific route designator of this route, intended for display + * @param designator the language-specific route designator of this route, intended for display; identical to + * {@code id} in practice (e.g., "22") * @param dataFeed the data feed identifier for this route, if applicable */ @NullMarked @@ -33,7 +34,8 @@ public record Route( * @param id the alphanumeric designator of the route (e.g., "22", "J14", "X9") * @param name the common name of the route (e.g., "Clark", "Jeffery Jump", "Ashland Express") * @param color the color of the route used in maps (e.g., "#ffffff") - * @param designator the language-specific route designator of the route, intended for display + * @param designator the language-specific route designator of the route, intended for display; identical to + * {@code id} in practice (e.g., "22") * @param dataFeed the data feed identifier for the route, if applicable * @throws NullPointerException if {@code id}, {@code name}, {@code color}, or {@code designator} is {@code null} */ diff --git a/src/main/java/com/cta4j/bus/stop/model/Stop.java b/src/main/java/com/cta4j/bus/stop/model/Stop.java index 9371251b..a9efa608 100644 --- a/src/main/java/com/cta4j/bus/stop/model/Stop.java +++ b/src/main/java/com/cta4j/bus/stop/model/Stop.java @@ -16,7 +16,7 @@ *

* * @param id the unique identifier of this stop - * @param name the display name of this stop (e.g. "Madison and Clark") + * @param name the display name of this stop (e.g., "Clark & Addison") * @param latitude the latitude coordinate of this stop * @param longitude the longitude coordinate of this stop * @param detoursAdded the {@link List} of detour IDs which temporarily add service to this stop @@ -39,7 +39,7 @@ public record Stop( * Constructs a {@code Stop}. * * @param id the unique identifier of the stop - * @param name the display name of the stop (e.g. "Madison and Clark") + * @param name the display name of the stop (e.g., "Clark & Addison") * @param latitude the latitude coordinate of the stop * @param longitude the longitude coordinate of the stop * @param detoursAdded the {@link List} of detour IDs which temporarily add service to the stop diff --git a/src/main/java/com/cta4j/bus/vehicle/model/Vehicle.java b/src/main/java/com/cta4j/bus/vehicle/model/Vehicle.java index 191113cd..4af9e2ab 100644 --- a/src/main/java/com/cta4j/bus/vehicle/model/Vehicle.java +++ b/src/main/java/com/cta4j/bus/vehicle/model/Vehicle.java @@ -10,7 +10,7 @@ * * @param id the unique identifier of this vehicle * @param routeId the alphanumeric designator of the route that is currently being serviced by this vehicle - * @param destination the destination of the trip being serviced by this vehicle (e.g. "Austin") + * @param destination the destination of the trip being serviced by this vehicle (e.g., "Howard") * @param coordinates the current coordinates of this vehicle * @param delayed whether this vehicle is currently delayed * @param metadata the metadata associated with this vehicle @@ -29,7 +29,7 @@ public record Vehicle( * * @param id the unique identifier of the vehicle * @param routeId the alphanumeric designator of the route that is currently being serviced by the vehicle - * @param destination the destination of the trip being serviced by the vehicle (e.g. "Austin") + * @param destination the destination of the trip being serviced by the vehicle (e.g., "Howard") * @param coordinates the current coordinates of the vehicle * @param delayed whether the vehicle is currently delayed * @param metadata the metadata associated with the vehicle diff --git a/src/main/java/com/cta4j/train/common/model/ArrivalMetadata.java b/src/main/java/com/cta4j/train/common/model/ArrivalMetadata.java index a941023b..ecd97fcc 100644 --- a/src/main/java/com/cta4j/train/common/model/ArrivalMetadata.java +++ b/src/main/java/com/cta4j/train/common/model/ArrivalMetadata.java @@ -9,6 +9,11 @@ /** * Represents metadata associated with a train arrival. * + *

+ * NOTE: {@code flags} is not well-documented by the CTA. As such, its presence here is primarily for + * completeness and may not be populated or described correctly. + *

+ * * @param runNumber the run number of the train associated with this arrival * @param direction the direction of travel of the train associated with this arrival * @param coordinates the coordinates of the train associated with this arrival, if applicable diff --git a/src/main/java/com/cta4j/train/location/model/LocationTrain.java b/src/main/java/com/cta4j/train/location/model/LocationTrain.java index a98344bd..d4e950f6 100644 --- a/src/main/java/com/cta4j/train/location/model/LocationTrain.java +++ b/src/main/java/com/cta4j/train/location/model/LocationTrain.java @@ -11,6 +11,11 @@ /** * Represents the location of a train on a route. * + *

+ * NOTE: {@code flags} is not well-documented by the CTA. As such, its presence here is primarily for + * completeness and may not be populated or described correctly. + *

+ * * @param run the run number of this train * @param destinationStationId the unique identifier of the destination station for this train * @param destinationName the display name of the destination station for this train From aa7a55ecb48c9a731907dd3e8cc18e886e750247 Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Sun, 19 Jul 2026 16:26:14 -0500 Subject: [PATCH 20/60] Refactor Javadoc comments for clarity in ImpactedService --- .../cta4j/alert/detailedalert/model/ImpactedService.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/cta4j/alert/detailedalert/model/ImpactedService.java b/src/main/java/com/cta4j/alert/detailedalert/model/ImpactedService.java index 2e3bb456..7257ada0 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/model/ImpactedService.java +++ b/src/main/java/com/cta4j/alert/detailedalert/model/ImpactedService.java @@ -10,11 +10,11 @@ * Represents a single service - a bus route, train route, train station, or systemwide grouping - impacted by * an alert. * - * @param type the type of service this represents + * @param type the type of service this service represents * @param name the name of this service (e.g., "Clark", "Red Line", "Jackson", "All Bus Routes") * @param serviceId the identifier of this service; matches GTFS route or station IDs, except for systemwide groupings, * which use a fixed identifier instead (e.g., "22", "Red", "Systemwide") - * @param color the color of this service used in maps; length and casing vary (e.g., "059", "565a5c") + * @param color the color of this service used in maps, as {@code rrggbb} (e.g., "565a5c") * @param textColor the suggested color of text displayed against {@code color}; casing varies (e.g., "ffffff", * "FFFFFF") * @param url the URL of this service's page on transitchicago.com @@ -31,11 +31,11 @@ public record ImpactedService( /** * Constructs an {@code ImpactedService}. * - * @param type the type of service the impacted service represents + * @param type the type of service the service represents * @param name the name of the service (e.g., "Clark", "Red Line", "Jackson", "All Bus Routes") * @param serviceId the identifier of the service; matches GTFS route or station IDs, except for systemwide * groupings, which use a fixed identifier instead (e.g., "22", "Red", "Systemwide") - * @param color the color of the service used in maps; length and casing vary (e.g., "059", "565a5c") + * @param color the color of the service used in maps, as {@code rrggbb} (e.g., "565a5c") * @param textColor the suggested color of text displayed against {@code color}; casing varies (e.g., "ffffff", * "FFFFFF") * @param url the URL of the service's page on transitchicago.com From 423b88102484e2a76dddaf9875da6b0bcc6bb0d5 Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Mon, 20 Jul 2026 12:40:11 -0500 Subject: [PATCH 21/60] Add detailed query classes for alerts: LineAlertsQuery, BusRouteAlertsQuery, and StationAlertsQuery --- .../detailedalert/DetailedAlertsApi.java | 12 + .../{AlertQuery.java => AlertsQuery.java} | 18 +- .../query/BusRouteAlertsQuery.java | 212 +++++++++++++++++ .../detailedalert/query/LineAlertsQuery.java | 213 ++++++++++++++++++ .../query/StationAlertsQuery.java | 212 +++++++++++++++++ 5 files changed, 658 insertions(+), 9 deletions(-) rename src/main/java/com/cta4j/alert/detailedalert/query/{AlertQuery.java => AlertsQuery.java} (94%) create mode 100644 src/main/java/com/cta4j/alert/detailedalert/query/BusRouteAlertsQuery.java create mode 100644 src/main/java/com/cta4j/alert/detailedalert/query/LineAlertsQuery.java create mode 100644 src/main/java/com/cta4j/alert/detailedalert/query/StationAlertsQuery.java diff --git a/src/main/java/com/cta4j/alert/detailedalert/DetailedAlertsApi.java b/src/main/java/com/cta4j/alert/detailedalert/DetailedAlertsApi.java index 184f7f4e..f4483593 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/DetailedAlertsApi.java +++ b/src/main/java/com/cta4j/alert/detailedalert/DetailedAlertsApi.java @@ -1,7 +1,19 @@ package com.cta4j.alert.detailedalert; +import com.cta4j.alert.detailedalert.model.Alert; +import com.cta4j.alert.detailedalert.query.AlertsQuery; import org.jspecify.annotations.NullMarked; +import java.util.List; + @NullMarked public interface DetailedAlertsApi { + List list(AlertsQuery query); + + default List list() { + AlertsQuery query = AlertsQuery.builder() + .build(); + + return this.list(query); + } } diff --git a/src/main/java/com/cta4j/alert/detailedalert/query/AlertQuery.java b/src/main/java/com/cta4j/alert/detailedalert/query/AlertsQuery.java similarity index 94% rename from src/main/java/com/cta4j/alert/detailedalert/query/AlertQuery.java rename to src/main/java/com/cta4j/alert/detailedalert/query/AlertsQuery.java index 82a9d1ed..ba1db206 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/query/AlertQuery.java +++ b/src/main/java/com/cta4j/alert/detailedalert/query/AlertsQuery.java @@ -17,7 +17,7 @@ * included */ @NullMarked -public record AlertQuery( +public record AlertsQuery( boolean activeOnly, boolean accessibility, boolean planned, @@ -25,7 +25,7 @@ public record AlertQuery( @Nullable Integer recentDays ) { /** - * Constructs an {@code AlertQuery}. + * Constructs an {@code AlertsQuery}. * * @param activeOnly whether to include only alerts that are currently active * @param accessibility whether to include alerts that affect accessible paths in stations @@ -36,7 +36,7 @@ public record AlertQuery( * @throws IllegalArgumentException if both {@code byStartDate} and {@code recentDays} are specified, or if * {@code recentDays} is non-{@code null} and not positive */ - public AlertQuery { + public AlertsQuery { if (byStartDate != null && recentDays != null) { throw new IllegalArgumentException("byStartDate and recentDays cannot both be specified"); } @@ -47,7 +47,7 @@ public record AlertQuery( } /** - * Creates a builder for {@code AlertQuery}. + * Creates a builder for {@code AlertsQuery}. * * @return a new {@code Builder} instance */ @@ -56,7 +56,7 @@ public static Builder builder() { } /** - * A builder for {@code AlertQuery}. + * A builder for {@code AlertsQuery}. */ public static final class Builder { /** @@ -167,13 +167,13 @@ public Builder recentDays(int recentDays) { } /** - * Builds the {@code AlertQuery}. + * Builds the {@code AlertsQuery}. * - * @return a new {@code AlertQuery} instance + * @return a new {@code AlertsQuery} instance * @throws IllegalArgumentException if both {@code byStartDate} and {@code recentDays} were specified */ - public AlertQuery build() { - return new AlertQuery( + public AlertsQuery build() { + return new AlertsQuery( this.activeOnly, this.accessibility, this.planned, diff --git a/src/main/java/com/cta4j/alert/detailedalert/query/BusRouteAlertsQuery.java b/src/main/java/com/cta4j/alert/detailedalert/query/BusRouteAlertsQuery.java new file mode 100644 index 00000000..0ed26cf9 --- /dev/null +++ b/src/main/java/com/cta4j/alert/detailedalert/query/BusRouteAlertsQuery.java @@ -0,0 +1,212 @@ +package com.cta4j.alert.detailedalert.query; + +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +import java.time.LocalDate; +import java.util.Collection; +import java.util.List; +import java.util.Objects; + +/** + * Represents a query for detailed bus route alerts. + * + * @param routeIds the {@link List} of bus route IDs to retrieve alerts for + * @param activeOnly whether to include only alerts that are currently active + * @param accessibility whether to include alerts that affect accessible paths in stations + * @param planned whether to include common planned alerts + * @param byStartDate the optional date; only alerts with a start date before this date are included + * @param recentDays the optional number of days; only alerts that started within this many days of today are + * included + */ +@NullMarked +public record BusRouteAlertsQuery( + List routeIds, + boolean activeOnly, + boolean accessibility, + boolean planned, + @Nullable LocalDate byStartDate, + @Nullable Integer recentDays +) { + /** + * Constructs a {@code BusRouteAlertsQuery}. + * + * @param routeIds the {@link List} of bus route IDs to retrieve alerts for + * @param activeOnly whether to include only alerts that are currently active + * @param accessibility whether to include alerts that affect accessible paths in stations + * @param planned whether to include common planned alerts + * @param byStartDate the optional date; only alerts with a start date before this date are included + * @param recentDays the optional number of days; only alerts that started within this many days of today are + * included + * @throws NullPointerException if {@code routeIds} is {@code null}, or if any element of {@code routeIds} is + * {@code null} + * @throws IllegalArgumentException if both {@code byStartDate} and {@code recentDays} are specified, or if + * {@code recentDays} is non-{@code null} and not positive + */ + public BusRouteAlertsQuery { + Objects.requireNonNull(routeIds); + + routeIds = List.copyOf(routeIds); + + if (byStartDate != null && recentDays != null) { + throw new IllegalArgumentException("byStartDate and recentDays cannot both be specified"); + } + + if (recentDays != null && recentDays <= 0) { + throw new IllegalArgumentException("recentDays must be positive"); + } + } + + /** + * Creates a builder for {@code BusRouteAlertsQuery}. + * + * @param routeIds the {@link Collection} of bus route IDs to retrieve alerts for + * @return a new {@code Builder} instance + * @throws NullPointerException if {@code routeIds} is {@code null}, or if any element of {@code routeIds} is + * {@code null} + */ + public static Builder builder(Collection routeIds) { + return new Builder(routeIds); + } + + /** + * A builder for {@code BusRouteAlertsQuery}. + */ + public static final class Builder { + /** + * The {@link List} of bus route IDs to retrieve alerts for. + */ + private final List routeIds; + + /** + * Whether to include only alerts that are currently active. + */ + private boolean activeOnly; + + /** + * Whether to include alerts that affect accessible paths in stations. + */ + private boolean accessibility; + + /** + * Whether to include common planned alerts. + */ + private boolean planned; + + /** + * The optional date; only alerts with a start date before this date are included. + */ + @Nullable + private LocalDate byStartDate; + + /** + * The optional number of days; only alerts that started within this many days of today are included. + */ + @Nullable + private Integer recentDays; + + /** + * Constructs a {@code Builder}. + *

+ * By default, {@code activeOnly} is {@code false}, and {@code accessibility} and {@code planned} are + * {@code true}, matching the CTA Alerts API's own defaults. + * + * @param routeIds the {@link Collection} of bus route IDs to retrieve alerts for + * @throws NullPointerException if {@code routeIds} is {@code null}, or if any element of {@code routeIds} is + * {@code null} + */ + public Builder(Collection routeIds) { + Objects.requireNonNull(routeIds); + + this.routeIds = List.copyOf(routeIds); + this.activeOnly = false; + this.accessibility = true; + this.planned = true; + } + + /** + * Sets whether to include only alerts that are currently active. + * + * @param activeOnly whether to include only active alerts + * @return this {@code Builder} instance + */ + public Builder activeOnly(boolean activeOnly) { + this.activeOnly = activeOnly; + + return this; + } + + /** + * Sets whether to include alerts that affect accessible paths in stations. + * + * @param accessibility whether to include accessibility-related alerts + * @return this {@code Builder} instance + */ + public Builder accessibility(boolean accessibility) { + this.accessibility = accessibility; + + return this; + } + + /** + * Sets whether to include common planned alerts. + * + * @param planned whether to include planned alerts + * @return this {@code Builder} instance + */ + public Builder planned(boolean planned) { + this.planned = planned; + + return this; + } + + /** + * Sets the date; only alerts with a start date before this date are included. + * + * @param byStartDate the date to filter alerts by + * @return this {@code Builder} instance + * @throws NullPointerException if {@code byStartDate} is {@code null} + */ + public Builder byStartDate(LocalDate byStartDate) { + Objects.requireNonNull(byStartDate); + + this.byStartDate = byStartDate; + + return this; + } + + /** + * Sets the number of days; only alerts that started within this many days of today are included. + * + * @param recentDays the number of days to filter alerts by + * @return this {@code Builder} instance + * @throws IllegalArgumentException if {@code recentDays} is not positive + */ + public Builder recentDays(int recentDays) { + if (recentDays <= 0) { + throw new IllegalArgumentException("recentDays must be positive"); + } + + this.recentDays = recentDays; + + return this; + } + + /** + * Builds the {@code BusRouteAlertsQuery}. + * + * @return a new {@code BusRouteAlertsQuery} instance + * @throws IllegalArgumentException if both {@code byStartDate} and {@code recentDays} were specified + */ + public BusRouteAlertsQuery build() { + return new BusRouteAlertsQuery( + this.routeIds, + this.activeOnly, + this.accessibility, + this.planned, + this.byStartDate, + this.recentDays + ); + } + } +} diff --git a/src/main/java/com/cta4j/alert/detailedalert/query/LineAlertsQuery.java b/src/main/java/com/cta4j/alert/detailedalert/query/LineAlertsQuery.java new file mode 100644 index 00000000..db125101 --- /dev/null +++ b/src/main/java/com/cta4j/alert/detailedalert/query/LineAlertsQuery.java @@ -0,0 +1,213 @@ +package com.cta4j.alert.detailedalert.query; + +import com.cta4j.common.train.TrainLine; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +import java.time.LocalDate; +import java.util.Collection; +import java.util.List; +import java.util.Objects; + +/** + * Represents a query for detailed train line alerts. + * + * @param lines the {@link List} of {@link TrainLine}s to retrieve alerts for + * @param activeOnly whether to include only alerts that are currently active + * @param accessibility whether to include alerts that affect accessible paths in stations + * @param planned whether to include common planned alerts + * @param byStartDate the optional date; only alerts with a start date before this date are included + * @param recentDays the optional number of days; only alerts that started within this many days of today are + * included + */ +@NullMarked +public record LineAlertsQuery( + List lines, + boolean activeOnly, + boolean accessibility, + boolean planned, + @Nullable LocalDate byStartDate, + @Nullable Integer recentDays +) { + /** + * Constructs a {@code LineAlertsQuery}. + * + * @param lines the {@link List} of {@link TrainLine}s to retrieve alerts for + * @param activeOnly whether to include only alerts that are currently active + * @param accessibility whether to include alerts that affect accessible paths in stations + * @param planned whether to include common planned alerts + * @param byStartDate the optional date; only alerts with a start date before this date are included + * @param recentDays the optional number of days; only alerts that started within this many days of today are + * included + * @throws NullPointerException if {@code lines} is {@code null}, or if any element of {@code lines} is + * {@code null} + * @throws IllegalArgumentException if both {@code byStartDate} and {@code recentDays} are specified, or if + * {@code recentDays} is non-{@code null} and not positive + */ + public LineAlertsQuery { + Objects.requireNonNull(lines); + + lines = List.copyOf(lines); + + if (byStartDate != null && recentDays != null) { + throw new IllegalArgumentException("byStartDate and recentDays cannot both be specified"); + } + + if (recentDays != null && recentDays <= 0) { + throw new IllegalArgumentException("recentDays must be positive"); + } + } + + /** + * Creates a builder for {@code LineAlertsQuery}. + * + * @param lines the {@link Collection} of {@link TrainLine}s to retrieve alerts for + * @return a new {@code Builder} instance + * @throws NullPointerException if {@code lines} is {@code null}, or if any element of {@code lines} is + * {@code null} + */ + public static Builder builder(Collection lines) { + return new Builder(lines); + } + + /** + * A builder for {@code LineAlertsQuery}. + */ + public static final class Builder { + /** + * The {@link List} of {@link TrainLine}s to retrieve alerts for. + */ + private final List lines; + + /** + * Whether to include only alerts that are currently active. + */ + private boolean activeOnly; + + /** + * Whether to include alerts that affect accessible paths in stations. + */ + private boolean accessibility; + + /** + * Whether to include common planned alerts. + */ + private boolean planned; + + /** + * The optional date; only alerts with a start date before this date are included. + */ + @Nullable + private LocalDate byStartDate; + + /** + * The optional number of days; only alerts that started within this many days of today are included. + */ + @Nullable + private Integer recentDays; + + /** + * Constructs a {@code Builder}. + *

+ * By default, {@code activeOnly} is {@code false}, and {@code accessibility} and {@code planned} are + * {@code true}, matching the CTA Alerts API's own defaults. + * + * @param lines the {@link Collection} of {@link TrainLine}s to retrieve alerts for + * @throws NullPointerException if {@code lines} is {@code null}, or if any element of {@code lines} is + * {@code null} + */ + public Builder(Collection lines) { + Objects.requireNonNull(lines); + + this.lines = List.copyOf(lines); + this.activeOnly = false; + this.accessibility = true; + this.planned = true; + } + + /** + * Sets whether to include only alerts that are currently active. + * + * @param activeOnly whether to include only active alerts + * @return this {@code Builder} instance + */ + public Builder activeOnly(boolean activeOnly) { + this.activeOnly = activeOnly; + + return this; + } + + /** + * Sets whether to include alerts that affect accessible paths in stations. + * + * @param accessibility whether to include accessibility-related alerts + * @return this {@code Builder} instance + */ + public Builder accessibility(boolean accessibility) { + this.accessibility = accessibility; + + return this; + } + + /** + * Sets whether to include common planned alerts. + * + * @param planned whether to include planned alerts + * @return this {@code Builder} instance + */ + public Builder planned(boolean planned) { + this.planned = planned; + + return this; + } + + /** + * Sets the date; only alerts with a start date before this date are included. + * + * @param byStartDate the date to filter alerts by + * @return this {@code Builder} instance + * @throws NullPointerException if {@code byStartDate} is {@code null} + */ + public Builder byStartDate(LocalDate byStartDate) { + Objects.requireNonNull(byStartDate); + + this.byStartDate = byStartDate; + + return this; + } + + /** + * Sets the number of days; only alerts that started within this many days of today are included. + * + * @param recentDays the number of days to filter alerts by + * @return this {@code Builder} instance + * @throws IllegalArgumentException if {@code recentDays} is not positive + */ + public Builder recentDays(int recentDays) { + if (recentDays <= 0) { + throw new IllegalArgumentException("recentDays must be positive"); + } + + this.recentDays = recentDays; + + return this; + } + + /** + * Builds the {@code LineAlertsQuery}. + * + * @return a new {@code LineAlertsQuery} instance + * @throws IllegalArgumentException if both {@code byStartDate} and {@code recentDays} were specified + */ + public LineAlertsQuery build() { + return new LineAlertsQuery( + this.lines, + this.activeOnly, + this.accessibility, + this.planned, + this.byStartDate, + this.recentDays + ); + } + } +} diff --git a/src/main/java/com/cta4j/alert/detailedalert/query/StationAlertsQuery.java b/src/main/java/com/cta4j/alert/detailedalert/query/StationAlertsQuery.java new file mode 100644 index 00000000..fe23feaa --- /dev/null +++ b/src/main/java/com/cta4j/alert/detailedalert/query/StationAlertsQuery.java @@ -0,0 +1,212 @@ +package com.cta4j.alert.detailedalert.query; + +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +import java.time.LocalDate; +import java.util.Collection; +import java.util.List; +import java.util.Objects; + +/** + * Represents a query for detailed train station alerts. + * + * @param stationIds the {@link List} of train station IDs to retrieve alerts for + * @param activeOnly whether to include only alerts that are currently active + * @param accessibility whether to include alerts that affect accessible paths in stations + * @param planned whether to include common planned alerts + * @param byStartDate the optional date; only alerts with a start date before this date are included + * @param recentDays the optional number of days; only alerts that started within this many days of today are + * included + */ +@NullMarked +public record StationAlertsQuery( + List stationIds, + boolean activeOnly, + boolean accessibility, + boolean planned, + @Nullable LocalDate byStartDate, + @Nullable Integer recentDays +) { + /** + * Constructs a {@code StationAlertsQuery}. + * + * @param stationIds the {@link List} of train station IDs to retrieve alerts for + * @param activeOnly whether to include only alerts that are currently active + * @param accessibility whether to include alerts that affect accessible paths in stations + * @param planned whether to include common planned alerts + * @param byStartDate the optional date; only alerts with a start date before this date are included + * @param recentDays the optional number of days; only alerts that started within this many days of today are + * included + * @throws NullPointerException if {@code stationIds} is {@code null}, or if any element of {@code stationIds} is + * {@code null} + * @throws IllegalArgumentException if both {@code byStartDate} and {@code recentDays} are specified, or if + * {@code recentDays} is non-{@code null} and not positive + */ + public StationAlertsQuery { + Objects.requireNonNull(stationIds); + + stationIds = List.copyOf(stationIds); + + if (byStartDate != null && recentDays != null) { + throw new IllegalArgumentException("byStartDate and recentDays cannot both be specified"); + } + + if (recentDays != null && recentDays <= 0) { + throw new IllegalArgumentException("recentDays must be positive"); + } + } + + /** + * Creates a builder for {@code StationAlertsQuery}. + * + * @param stationIds the {@link Collection} of train station IDs to retrieve alerts for + * @return a new {@code Builder} instance + * @throws NullPointerException if {@code stationIds} is {@code null}, or if any element of {@code stationIds} is + * {@code null} + */ + public static Builder builder(Collection stationIds) { + return new Builder(stationIds); + } + + /** + * A builder for {@code StationAlertsQuery}. + */ + public static final class Builder { + /** + * The {@link List} of train station IDs to retrieve alerts for. + */ + private final List stationIds; + + /** + * Whether to include only alerts that are currently active. + */ + private boolean activeOnly; + + /** + * Whether to include alerts that affect accessible paths in stations. + */ + private boolean accessibility; + + /** + * Whether to include common planned alerts. + */ + private boolean planned; + + /** + * The optional date; only alerts with a start date before this date are included. + */ + @Nullable + private LocalDate byStartDate; + + /** + * The optional number of days; only alerts that started within this many days of today are included. + */ + @Nullable + private Integer recentDays; + + /** + * Constructs a {@code Builder}. + *

+ * By default, {@code activeOnly} is {@code false}, and {@code accessibility} and {@code planned} are + * {@code true}, matching the CTA Alerts API's own defaults. + * + * @param stationIds the {@link Collection} of train station IDs to retrieve alerts for + * @throws NullPointerException if {@code stationIds} is {@code null}, or if any element of {@code stationIds} is + * {@code null} + */ + public Builder(Collection stationIds) { + Objects.requireNonNull(stationIds); + + this.stationIds = List.copyOf(stationIds); + this.activeOnly = false; + this.accessibility = true; + this.planned = true; + } + + /** + * Sets whether to include only alerts that are currently active. + * + * @param activeOnly whether to include only active alerts + * @return this {@code Builder} instance + */ + public Builder activeOnly(boolean activeOnly) { + this.activeOnly = activeOnly; + + return this; + } + + /** + * Sets whether to include alerts that affect accessible paths in stations. + * + * @param accessibility whether to include accessibility-related alerts + * @return this {@code Builder} instance + */ + public Builder accessibility(boolean accessibility) { + this.accessibility = accessibility; + + return this; + } + + /** + * Sets whether to include common planned alerts. + * + * @param planned whether to include planned alerts + * @return this {@code Builder} instance + */ + public Builder planned(boolean planned) { + this.planned = planned; + + return this; + } + + /** + * Sets the date; only alerts with a start date before this date are included. + * + * @param byStartDate the date to filter alerts by + * @return this {@code Builder} instance + * @throws NullPointerException if {@code byStartDate} is {@code null} + */ + public Builder byStartDate(LocalDate byStartDate) { + Objects.requireNonNull(byStartDate); + + this.byStartDate = byStartDate; + + return this; + } + + /** + * Sets the number of days; only alerts that started within this many days of today are included. + * + * @param recentDays the number of days to filter alerts by + * @return this {@code Builder} instance + * @throws IllegalArgumentException if {@code recentDays} is not positive + */ + public Builder recentDays(int recentDays) { + if (recentDays <= 0) { + throw new IllegalArgumentException("recentDays must be positive"); + } + + this.recentDays = recentDays; + + return this; + } + + /** + * Builds the {@code StationAlertsQuery}. + * + * @return a new {@code StationAlertsQuery} instance + * @throws IllegalArgumentException if both {@code byStartDate} and {@code recentDays} were specified + */ + public StationAlertsQuery build() { + return new StationAlertsQuery( + this.stationIds, + this.activeOnly, + this.accessibility, + this.planned, + this.byStartDate, + this.recentDays + ); + } + } +} From b3e3409503e0abc4a65cc70760eef4116439c0f8 Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Mon, 20 Jul 2026 19:48:04 -0500 Subject: [PATCH 22/60] Add methods for querying alerts by bus route, line, and station IDs in DetailedAlertsApi --- .../detailedalert/DetailedAlertsApi.java | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/src/main/java/com/cta4j/alert/detailedalert/DetailedAlertsApi.java b/src/main/java/com/cta4j/alert/detailedalert/DetailedAlertsApi.java index f4483593..c0cecc69 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/DetailedAlertsApi.java +++ b/src/main/java/com/cta4j/alert/detailedalert/DetailedAlertsApi.java @@ -2,9 +2,15 @@ import com.cta4j.alert.detailedalert.model.Alert; import com.cta4j.alert.detailedalert.query.AlertsQuery; +import com.cta4j.alert.detailedalert.query.BusRouteAlertsQuery; +import com.cta4j.alert.detailedalert.query.LineAlertsQuery; +import com.cta4j.alert.detailedalert.query.StationAlertsQuery; +import com.cta4j.common.train.TrainLine; import org.jspecify.annotations.NullMarked; +import java.util.Collection; import java.util.List; +import java.util.Objects; @NullMarked public interface DetailedAlertsApi { @@ -16,4 +22,67 @@ default List list() { return this.list(query); } + + List findByBusRouteIds(BusRouteAlertsQuery query); + + default List findByBusRouteIds(Collection routeIds) { + Objects.requireNonNull(routeIds); + + List routeIdsList = List.copyOf(routeIds); + + BusRouteAlertsQuery query = BusRouteAlertsQuery.builder(routeIdsList) + .build(); + + return this.findByBusRouteIds(query); + } + + default List findByBusRouteId(String routeId) { + Objects.requireNonNull(routeId); + + List routeIds = List.of(routeId); + + return this.findByBusRouteIds(routeIds); + } + + List findByLines(LineAlertsQuery query); + + default List findByLines(Collection lines) { + Objects.requireNonNull(lines); + + List linesList = List.copyOf(lines); + + LineAlertsQuery query = LineAlertsQuery.builder(linesList) + .build(); + + return this.findByLines(query); + } + + default List findByLine(TrainLine line) { + Objects.requireNonNull(line); + + List lines = List.of(line); + + return this.findByLines(lines); + } + + List findByStationIds(StationAlertsQuery query); + + default List findByStationIds(Collection stationIds) { + Objects.requireNonNull(stationIds); + + List stationIdsList = List.copyOf(stationIds); + + StationAlertsQuery query = StationAlertsQuery.builder(stationIdsList) + .build(); + + return this.findByStationIds(query); + } + + default List findByStationId(String stationId) { + Objects.requireNonNull(stationId); + + List stationIds = List.of(stationId); + + return this.findByStationIds(stationIds); + } } From ddd80494c644da43af5384f456658d4fa3e9fb42 Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Wed, 22 Jul 2026 14:23:31 -0500 Subject: [PATCH 23/60] Rename query classes for consistency: StopsPredictionsQuery to StopPredictionsQuery, VehiclesPredictionsQuery to VehiclePredictionsQuery, MapArrivalQuery to MapArrivalsQuery, and StopArrivalQuery to StopArrivalsQuery. Update references throughout the codebase. --- .../cta4j/bus/prediction/PredictionsApi.java | 22 ++++----- .../internal/impl/PredictionsApiImpl.java | 8 ++-- ...nsQuery.java => StopPredictionsQuery.java} | 33 ++++++------- ...uery.java => VehiclePredictionsQuery.java} | 27 ++++++----- .../com/cta4j/train/arrival/ArrivalsApi.java | 16 +++---- .../internal/impl/ArrivalsApiImpl.java | 8 ++-- ...rrivalQuery.java => MapArrivalsQuery.java} | 18 +++---- ...rivalQuery.java => StopArrivalsQuery.java} | 18 +++---- .../prediction/PredictionsApiImplTest.java | 40 ++++++++-------- ...est.java => StopPredictionsQueryTest.java} | 20 ++++---- ....java => VehiclePredictionsQueryTest.java} | 18 +++---- .../train/arrival/ArrivalsApiImplTest.java | 48 +++++++++---------- ...eryTest.java => MapArrivalsQueryTest.java} | 18 +++---- ...ryTest.java => StopArrivalsQueryTest.java} | 18 +++---- 14 files changed, 157 insertions(+), 155 deletions(-) rename src/main/java/com/cta4j/bus/prediction/query/{StopsPredictionsQuery.java => StopPredictionsQuery.java} (79%) rename src/main/java/com/cta4j/bus/prediction/query/{VehiclesPredictionsQuery.java => VehiclePredictionsQuery.java} (79%) rename src/main/java/com/cta4j/train/arrival/query/{MapArrivalQuery.java => MapArrivalsQuery.java} (89%) rename src/main/java/com/cta4j/train/arrival/query/{StopArrivalQuery.java => StopArrivalsQuery.java} (89%) rename src/test/java/com/cta4j/bus/prediction/query/{StopsPredictionsQueryTest.java => StopPredictionsQueryTest.java} (65%) rename src/test/java/com/cta4j/bus/prediction/query/{VehiclesPredictionsQueryTest.java => VehiclePredictionsQueryTest.java} (65%) rename src/test/java/com/cta4j/train/arrival/query/{MapArrivalQueryTest.java => MapArrivalsQueryTest.java} (66%) rename src/test/java/com/cta4j/train/arrival/query/{StopArrivalQueryTest.java => StopArrivalsQueryTest.java} (66%) diff --git a/src/main/java/com/cta4j/bus/prediction/PredictionsApi.java b/src/main/java/com/cta4j/bus/prediction/PredictionsApi.java index aae4871c..54d802b6 100644 --- a/src/main/java/com/cta4j/bus/prediction/PredictionsApi.java +++ b/src/main/java/com/cta4j/bus/prediction/PredictionsApi.java @@ -2,8 +2,8 @@ import com.cta4j.bus.common.exception.Cta4jBusException; import com.cta4j.bus.prediction.model.Prediction; -import com.cta4j.bus.prediction.query.StopsPredictionsQuery; -import com.cta4j.bus.prediction.query.VehiclesPredictionsQuery; +import com.cta4j.bus.prediction.query.StopPredictionsQuery; +import com.cta4j.bus.prediction.query.VehiclePredictionsQuery; import org.jspecify.annotations.NullMarked; import java.util.List; @@ -25,7 +25,7 @@ public interface PredictionsApi { * @throws NullPointerException if {@code query} is {@code null} * @throws Cta4jBusException if the API returns an error response or the response cannot be parsed */ - List findByStopIds(StopsPredictionsQuery query); + List findByStopIds(StopPredictionsQuery query); /** * Retrieves predictions by vehicle IDs. @@ -36,7 +36,7 @@ public interface PredictionsApi { * @throws NullPointerException if {@code query} is {@code null} * @throws Cta4jBusException if the API returns an error response or the response cannot be parsed */ - List findByVehicleIds(VehiclesPredictionsQuery query); + List findByVehicleIds(VehiclePredictionsQuery query); /** * Retrieves predictions by stop ID. @@ -52,8 +52,8 @@ default List findByStopId(String stopId) { List stopIds = List.of(stopId); - StopsPredictionsQuery query = StopsPredictionsQuery.builder(stopIds) - .build(); + StopPredictionsQuery query = StopPredictionsQuery.builder(stopIds) + .build(); return this.findByStopIds(query); } @@ -75,9 +75,9 @@ default List findByRouteIdAndStopId(String routeId, String stopId) { List stopIds = List.of(stopId); List routeIds = List.of(routeId); - StopsPredictionsQuery query = StopsPredictionsQuery.builder(stopIds) - .routeIds(routeIds) - .build(); + StopPredictionsQuery query = StopPredictionsQuery.builder(stopIds) + .routeIds(routeIds) + .build(); return this.findByStopIds(query); } @@ -96,8 +96,8 @@ default List findByVehicleId(String vehicleId) { List vehicleIds = List.of(vehicleId); - VehiclesPredictionsQuery query = VehiclesPredictionsQuery.builder(vehicleIds) - .build(); + VehiclePredictionsQuery query = VehiclePredictionsQuery.builder(vehicleIds) + .build(); return this.findByVehicleIds(query); } diff --git a/src/main/java/com/cta4j/bus/prediction/internal/impl/PredictionsApiImpl.java b/src/main/java/com/cta4j/bus/prediction/internal/impl/PredictionsApiImpl.java index 8ab371cf..7ec479ba 100644 --- a/src/main/java/com/cta4j/bus/prediction/internal/impl/PredictionsApiImpl.java +++ b/src/main/java/com/cta4j/bus/prediction/internal/impl/PredictionsApiImpl.java @@ -11,8 +11,8 @@ import com.cta4j.bus.prediction.internal.wire.CtaPredictionBustimeResponse; import com.cta4j.bus.prediction.internal.wire.CtaPredictionError; import com.cta4j.bus.prediction.model.Prediction; -import com.cta4j.bus.prediction.query.StopsPredictionsQuery; -import com.cta4j.bus.prediction.query.VehiclesPredictionsQuery; +import com.cta4j.bus.prediction.query.StopPredictionsQuery; +import com.cta4j.bus.prediction.query.VehiclePredictionsQuery; import org.apache.hc.client5.http.fluent.Request; import org.apache.hc.core5.net.URIBuilder; import org.jetbrains.annotations.ApiStatus; @@ -38,7 +38,7 @@ public PredictionsApiImpl(BusApiConfig config) { } @Override - public List findByStopIds(StopsPredictionsQuery query) { + public List findByStopIds(StopPredictionsQuery query) { Objects.requireNonNull(query); List stopIds = query.stopIds(); @@ -77,7 +77,7 @@ public List findByStopIds(StopsPredictionsQuery query) { } @Override - public List findByVehicleIds(VehiclesPredictionsQuery query) { + public List findByVehicleIds(VehiclePredictionsQuery query) { Objects.requireNonNull(query); List vehicleIds = query.vehicleIds(); diff --git a/src/main/java/com/cta4j/bus/prediction/query/StopsPredictionsQuery.java b/src/main/java/com/cta4j/bus/prediction/query/StopPredictionsQuery.java similarity index 79% rename from src/main/java/com/cta4j/bus/prediction/query/StopsPredictionsQuery.java rename to src/main/java/com/cta4j/bus/prediction/query/StopPredictionsQuery.java index 2c2c8e4d..236a7b89 100644 --- a/src/main/java/com/cta4j/bus/prediction/query/StopsPredictionsQuery.java +++ b/src/main/java/com/cta4j/bus/prediction/query/StopPredictionsQuery.java @@ -4,6 +4,7 @@ import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; +import java.util.Collection; import java.util.List; import java.util.Objects; @@ -15,13 +16,13 @@ * @param maxResults the optional maximum number of predictions to return */ @NullMarked -public record StopsPredictionsQuery( +public record StopPredictionsQuery( List stopIds, @Nullable List routeIds, @Nullable Integer maxResults ) { /** - * Constructs a {@code StopsPredictionsQuery}. + * Constructs a {@code StopPredictionsQuery}. * * @param stopIds the {@link List} of stop IDs to retrieve predictions for * @param routeIds the optional {@link List} of route IDs to filter predictions by @@ -31,7 +32,7 @@ public record StopsPredictionsQuery( * @throws IllegalArgumentException if more than 10 stop IDs are provided, or if {@code maxResults} is * non-{@code null} and not positive */ - public StopsPredictionsQuery { + public StopPredictionsQuery { Objects.requireNonNull(stopIds); ApiUtils.requireMaxIds(stopIds, "stop"); @@ -48,19 +49,19 @@ public record StopsPredictionsQuery( } /** - * Creates a builder for {@code StopsPredictionsQuery}. + * Creates a builder for {@code StopPredictionsQuery}. * - * @param stopIds the {@link List} of stop IDs to retrieve predictions for + * @param stopIds the {@link Collection} of stop IDs to retrieve predictions for * @return a new {@code Builder} instance * @throws NullPointerException if {@code stopIds} is {@code null}, or if any element of {@code stopIds} is * {@code null} */ - public static Builder builder(List stopIds) { + public static Builder builder(Collection stopIds) { return new Builder(stopIds); } /** - * A builder for {@code StopsPredictionsQuery}. + * A builder for {@code StopPredictionsQuery}. */ public static final class Builder { /** @@ -83,25 +84,25 @@ public static final class Builder { /** * Constructs a {@code Builder}. * - * @param stopIds the {@link List} of stop IDs to retrieve predictions for + * @param stopIds the {@link Collection} of stop IDs to retrieve predictions for * @throws NullPointerException if {@code stopIds} is {@code null}, or if any element of {@code stopIds} is * {@code null} */ - public Builder(List stopIds) { + public Builder(Collection stopIds) { Objects.requireNonNull(stopIds); this.stopIds = List.copyOf(stopIds); } /** - * Sets the {@link List} of route IDs to filter predictions by. + * Sets the {@link Collection} of route IDs to filter predictions by. * - * @param routeIds the {@link List} of route IDs + * @param routeIds the {@link Collection} of route IDs * @return this {@code Builder} instance * @throws NullPointerException if {@code routeIds} is {@code null}, or if any element of {@code routeIds} is * {@code null} */ - public Builder routeIds(List routeIds) { + public Builder routeIds(Collection routeIds) { Objects.requireNonNull(routeIds); this.routeIds = List.copyOf(routeIds); @@ -127,13 +128,13 @@ public Builder maxResults(int maxResults) { } /** - * Builds the {@code StopsPredictionsQuery}. + * Builds the {@code StopPredictionsQuery}. * - * @return a new {@code StopsPredictionsQuery} instance + * @return a new {@code StopPredictionsQuery} instance * @throws IllegalArgumentException if more than 10 stop IDs are provided */ - public StopsPredictionsQuery build() { - return new StopsPredictionsQuery( + public StopPredictionsQuery build() { + return new StopPredictionsQuery( this.stopIds, this.routeIds, this.maxResults diff --git a/src/main/java/com/cta4j/bus/prediction/query/VehiclesPredictionsQuery.java b/src/main/java/com/cta4j/bus/prediction/query/VehiclePredictionsQuery.java similarity index 79% rename from src/main/java/com/cta4j/bus/prediction/query/VehiclesPredictionsQuery.java rename to src/main/java/com/cta4j/bus/prediction/query/VehiclePredictionsQuery.java index 0a09a488..63aa0d0d 100644 --- a/src/main/java/com/cta4j/bus/prediction/query/VehiclesPredictionsQuery.java +++ b/src/main/java/com/cta4j/bus/prediction/query/VehiclePredictionsQuery.java @@ -4,6 +4,7 @@ import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; +import java.util.Collection; import java.util.List; import java.util.Objects; @@ -14,12 +15,12 @@ * @param maxResults the optional maximum number of predictions to return */ @NullMarked -public record VehiclesPredictionsQuery( +public record VehiclePredictionsQuery( List vehicleIds, @Nullable Integer maxResults ) { /** - * Constructs a {@code VehiclesPredictionsQuery}. + * Constructs a {@code VehiclePredictionsQuery}. * * @param vehicleIds the {@link List} of vehicle IDs to retrieve predictions for * @param maxResults the optional maximum number of predictions to return @@ -28,7 +29,7 @@ public record VehiclesPredictionsQuery( * @throws IllegalArgumentException if more than 10 vehicle IDs are provided, or if {@code maxResults} is * non-{@code null} and not positive */ - public VehiclesPredictionsQuery { + public VehiclePredictionsQuery { Objects.requireNonNull(vehicleIds); ApiUtils.requireMaxIds(vehicleIds, "vehicle"); @@ -41,19 +42,19 @@ public record VehiclesPredictionsQuery( } /** - * Creates a builder for {@code VehiclesPredictionsQuery}. + * Creates a builder for {@code VehiclePredictionsQuery}. * - * @param vehicleIds the {@link List} of vehicle IDs to retrieve predictions for + * @param vehicleIds the {@link Collection} of vehicle IDs to retrieve predictions for * @return a new {@code Builder} instance * @throws NullPointerException if {@code vehicleIds} is {@code null}, or if any element of {@code vehicleIds} is * {@code null} */ - public static Builder builder(List vehicleIds) { + public static Builder builder(Collection vehicleIds) { return new Builder(vehicleIds); } /** - * Builder for {@code VehiclesPredictionsQuery}. + * Builder for {@code VehiclePredictionsQuery}. */ public static final class Builder { /** @@ -70,11 +71,11 @@ public static final class Builder { /** * Constructs a {@code Builder}. * - * @param vehicleIds the {@link List} of vehicle IDs to retrieve predictions for + * @param vehicleIds the {@link Collection} of vehicle IDs to retrieve predictions for * @throws NullPointerException if {@code vehicleIds} is {@code null}, or if any element of * {@code vehicleIds} is {@code null} */ - public Builder(List vehicleIds) { + public Builder(Collection vehicleIds) { Objects.requireNonNull(vehicleIds); this.vehicleIds = List.copyOf(vehicleIds); @@ -98,13 +99,13 @@ public Builder maxResults(int maxResults) { } /** - * Builds the {@code VehiclesPredictionsQuery}. + * Builds the {@code VehiclePredictionsQuery}. * - * @return the constructed {@code VehiclesPredictionsQuery} + * @return the constructed {@code VehiclePredictionsQuery} * @throws IllegalArgumentException if more than 10 vehicle IDs are provided */ - public VehiclesPredictionsQuery build() { - return new VehiclesPredictionsQuery( + public VehiclePredictionsQuery build() { + return new VehiclePredictionsQuery( this.vehicleIds, this.maxResults ); diff --git a/src/main/java/com/cta4j/train/arrival/ArrivalsApi.java b/src/main/java/com/cta4j/train/arrival/ArrivalsApi.java index 1bf5fab0..c384a2a0 100644 --- a/src/main/java/com/cta4j/train/arrival/ArrivalsApi.java +++ b/src/main/java/com/cta4j/train/arrival/ArrivalsApi.java @@ -1,8 +1,8 @@ package com.cta4j.train.arrival; import com.cta4j.train.arrival.exception.Cta4jArrivalsException; -import com.cta4j.train.arrival.query.MapArrivalQuery; -import com.cta4j.train.arrival.query.StopArrivalQuery; +import com.cta4j.train.arrival.query.MapArrivalsQuery; +import com.cta4j.train.arrival.query.StopArrivalsQuery; import com.cta4j.train.common.model.Arrival; import org.jspecify.annotations.NullMarked; @@ -24,7 +24,7 @@ public interface ArrivalsApi { * @throws NullPointerException if {@code query} is {@code null} * @throws Cta4jArrivalsException if the API returns an error response or the response cannot be parsed */ - List findByMapId(MapArrivalQuery query); + List findByMapId(MapArrivalsQuery query); /** * Retrieves arrivals by stop ID. @@ -35,7 +35,7 @@ public interface ArrivalsApi { * @throws NullPointerException if {@code query} is {@code null} * @throws Cta4jArrivalsException if the API returns an error response or the response cannot be parsed */ - List findByStopId(StopArrivalQuery query); + List findByStopId(StopArrivalsQuery query); /** * Retrieves arrivals by map ID. @@ -47,8 +47,8 @@ public interface ArrivalsApi { * @throws Cta4jArrivalsException if the API returns an error response or the response cannot be parsed */ default List findByMapId(String mapId) { - MapArrivalQuery query = MapArrivalQuery.builder(mapId) - .build(); + MapArrivalsQuery query = MapArrivalsQuery.builder(mapId) + .build(); return this.findByMapId(query); } @@ -63,8 +63,8 @@ default List findByMapId(String mapId) { * @throws Cta4jArrivalsException if the API returns an error response or the response cannot be parsed */ default List findByStopId(String stopId) { - StopArrivalQuery query = StopArrivalQuery.builder(stopId) - .build(); + StopArrivalsQuery query = StopArrivalsQuery.builder(stopId) + .build(); return this.findByStopId(query); } diff --git a/src/main/java/com/cta4j/train/arrival/internal/impl/ArrivalsApiImpl.java b/src/main/java/com/cta4j/train/arrival/internal/impl/ArrivalsApiImpl.java index 603b4f33..4adcb454 100644 --- a/src/main/java/com/cta4j/train/arrival/internal/impl/ArrivalsApiImpl.java +++ b/src/main/java/com/cta4j/train/arrival/internal/impl/ArrivalsApiImpl.java @@ -4,8 +4,8 @@ import com.cta4j.train.arrival.exception.ArrivalsErrorCode; import com.cta4j.train.arrival.exception.Cta4jArrivalsException; import com.cta4j.train.arrival.internal.wire.CtaArrivalsResponse; -import com.cta4j.train.arrival.query.MapArrivalQuery; -import com.cta4j.train.arrival.query.StopArrivalQuery; +import com.cta4j.train.arrival.query.MapArrivalsQuery; +import com.cta4j.train.arrival.query.StopArrivalsQuery; import com.cta4j.train.common.internal.config.TrainApiConfig; import com.cta4j.train.common.internal.mapper.ArrivalMapper; import com.cta4j.train.common.internal.util.TrainApiConstants; @@ -38,7 +38,7 @@ public ArrivalsApiImpl(TrainApiConfig config) { } @Override - public List findByMapId(MapArrivalQuery query) { + public List findByMapId(MapArrivalsQuery query) { Objects.requireNonNull(query); URIBuilder builder = new URIBuilder() @@ -54,7 +54,7 @@ public List findByMapId(MapArrivalQuery query) { } @Override - public List findByStopId(StopArrivalQuery query) { + public List findByStopId(StopArrivalsQuery query) { Objects.requireNonNull(query); URIBuilder builder = new URIBuilder() diff --git a/src/main/java/com/cta4j/train/arrival/query/MapArrivalQuery.java b/src/main/java/com/cta4j/train/arrival/query/MapArrivalsQuery.java similarity index 89% rename from src/main/java/com/cta4j/train/arrival/query/MapArrivalQuery.java rename to src/main/java/com/cta4j/train/arrival/query/MapArrivalsQuery.java index d1729500..af2fe7af 100644 --- a/src/main/java/com/cta4j/train/arrival/query/MapArrivalQuery.java +++ b/src/main/java/com/cta4j/train/arrival/query/MapArrivalsQuery.java @@ -14,13 +14,13 @@ * @param maxResults the optional maximum number of arrivals to return */ @NullMarked -public record MapArrivalQuery( +public record MapArrivalsQuery( String mapId, @Nullable TrainLine line, @Nullable Integer maxResults ) { /** - * Constructs a {@code MapArrivalQuery}. + * Constructs a {@code MapArrivalsQuery}. * * @param mapId the ID of the map to retrieve arrivals for * @param line the optional train line to filter arrivals by @@ -28,7 +28,7 @@ public record MapArrivalQuery( * @throws NullPointerException if {@code mapId} is {@code null} * @throws IllegalArgumentException if {@code maxResults} is non-{@code null} and not positive */ - public MapArrivalQuery { + public MapArrivalsQuery { Objects.requireNonNull(mapId); if ((maxResults != null) && (maxResults <= 0)) { @@ -37,7 +37,7 @@ public record MapArrivalQuery( } /** - * Creates a builder for {@code MapArrivalQuery}. + * Creates a builder for {@code MapArrivalsQuery}. * * @param mapId the ID of the map to retrieve arrivals for * @return a new {@code Builder} instance @@ -48,7 +48,7 @@ public static Builder builder(String mapId) { } /** - * A builder for {@code MapArrivalQuery}. + * A builder for {@code MapArrivalsQuery}. */ public static final class Builder { /** @@ -109,12 +109,12 @@ public Builder maxResults(int maxResults) { } /** - * Builds the {@code MapArrivalQuery}. + * Builds the {@code MapArrivalsQuery}. * - * @return a new {@code MapArrivalQuery} instance + * @return a new {@code MapArrivalsQuery} instance */ - public MapArrivalQuery build() { - return new MapArrivalQuery( + public MapArrivalsQuery build() { + return new MapArrivalsQuery( this.mapId, this.line, this.maxResults diff --git a/src/main/java/com/cta4j/train/arrival/query/StopArrivalQuery.java b/src/main/java/com/cta4j/train/arrival/query/StopArrivalsQuery.java similarity index 89% rename from src/main/java/com/cta4j/train/arrival/query/StopArrivalQuery.java rename to src/main/java/com/cta4j/train/arrival/query/StopArrivalsQuery.java index ece9c3e5..94525bd8 100644 --- a/src/main/java/com/cta4j/train/arrival/query/StopArrivalQuery.java +++ b/src/main/java/com/cta4j/train/arrival/query/StopArrivalsQuery.java @@ -14,13 +14,13 @@ * @param maxResults the optional maximum number of arrivals to return */ @NullMarked -public record StopArrivalQuery( +public record StopArrivalsQuery( String stopId, @Nullable TrainLine line, @Nullable Integer maxResults ) { /** - * Constructs a {@code StopArrivalQuery}. + * Constructs a {@code StopArrivalsQuery}. * * @param stopId the ID of the stop to retrieve arrivals for * @param line the optional train line to filter arrivals by @@ -28,7 +28,7 @@ public record StopArrivalQuery( * @throws NullPointerException if {@code stopId} is {@code null} * @throws IllegalArgumentException if {@code maxResults} is non-{@code null} and not positive */ - public StopArrivalQuery { + public StopArrivalsQuery { Objects.requireNonNull(stopId); if ((maxResults != null) && (maxResults <= 0)) { @@ -37,7 +37,7 @@ public record StopArrivalQuery( } /** - * Creates a builder for {@code StopArrivalQuery}. + * Creates a builder for {@code StopArrivalsQuery}. * * @param stopId the ID of the stop to retrieve arrivals for * @return a new {@code Builder} instance @@ -48,7 +48,7 @@ public static Builder builder(String stopId) { } /** - * A builder for {@code StopArrivalQuery}. + * A builder for {@code StopArrivalsQuery}. */ public static final class Builder { /** @@ -109,12 +109,12 @@ public Builder maxResults(int maxResults) { } /** - * Builds the {@code StopArrivalQuery}. + * Builds the {@code StopArrivalsQuery}. * - * @return a new {@code StopArrivalQuery} instance + * @return a new {@code StopArrivalsQuery} instance */ - public StopArrivalQuery build() { - return new StopArrivalQuery( + public StopArrivalsQuery build() { + return new StopArrivalsQuery( this.stopId, this.line, this.maxResults diff --git a/src/test/java/com/cta4j/bus/prediction/PredictionsApiImplTest.java b/src/test/java/com/cta4j/bus/prediction/PredictionsApiImplTest.java index 0de8407d..e668f034 100644 --- a/src/test/java/com/cta4j/bus/prediction/PredictionsApiImplTest.java +++ b/src/test/java/com/cta4j/bus/prediction/PredictionsApiImplTest.java @@ -6,8 +6,8 @@ import com.cta4j.bus.common.internal.util.BusApiConstants; import com.cta4j.bus.prediction.internal.impl.PredictionsApiImpl; import com.cta4j.bus.prediction.model.Prediction; -import com.cta4j.bus.prediction.query.StopsPredictionsQuery; -import com.cta4j.bus.prediction.query.VehiclesPredictionsQuery; +import com.cta4j.bus.prediction.query.StopPredictionsQuery; +import com.cta4j.bus.prediction.query.VehiclePredictionsQuery; import com.github.tomakehurst.wiremock.WireMockServer; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -45,7 +45,7 @@ void findByStopIds_returnsPredictions_whenResponseContainsPredictions() { .withHeader("Content-Type", "application/json") .withBody(TestFixtures.read("bus/prediction/success.json")))); - StopsPredictionsQuery query = StopsPredictionsQuery.builder(List.of("456")).build(); + StopPredictionsQuery query = StopPredictionsQuery.builder(List.of("456")).build(); List predictions = this.api.findByStopIds(query); assertThat(predictions).hasSize(1); @@ -57,7 +57,7 @@ void findByStopIds_returnsPredictions_whenResponseContainsPredictions() { @Test void findByStopIds_returnsEmpty_whenInputIsEmpty() { - StopsPredictionsQuery query = StopsPredictionsQuery.builder(List.of()).build(); + StopPredictionsQuery query = StopPredictionsQuery.builder(List.of()).build(); List predictions = this.api.findByStopIds(query); assertThat(predictions).isEmpty(); @@ -72,7 +72,7 @@ void findByStopIds_returnsEmpty_whenResponseHasNoDataAndNoErrors() { .withHeader("Content-Type", "application/json") .withBody(TestFixtures.read("bus/prediction/empty.json")))); - StopsPredictionsQuery query = StopsPredictionsQuery.builder(List.of("456")).build(); + StopPredictionsQuery query = StopPredictionsQuery.builder(List.of("456")).build(); List predictions = this.api.findByStopIds(query); assertThat(predictions).isEmpty(); @@ -86,7 +86,7 @@ void findByStopIds_returnsEmpty_whenAllErrorsAreResourceSpecific() { .withHeader("Content-Type", "application/json") .withBody(TestFixtures.read("bus/prediction/not_found.json")))); - StopsPredictionsQuery query = StopsPredictionsQuery.builder(List.of("99999")).build(); + StopPredictionsQuery query = StopPredictionsQuery.builder(List.of("99999")).build(); List predictions = this.api.findByStopIds(query); assertThat(predictions).isEmpty(); @@ -100,7 +100,7 @@ void findByStopIds_throwsCta4jBusException_whenResponseContainsFatalErrors() { .withHeader("Content-Type", "application/json") .withBody(TestFixtures.read("bus/prediction/error.json")))); - StopsPredictionsQuery query = StopsPredictionsQuery.builder(List.of("456")).build(); + StopPredictionsQuery query = StopPredictionsQuery.builder(List.of("456")).build(); assertThatThrownBy(() -> this.api.findByStopIds(query)) .isInstanceOf(Cta4jBusException.class) @@ -117,7 +117,7 @@ void findByStopIds_throwsCta4jBusException_whenResponseIsNotJson() { .withHeader("Content-Type", "application/json") .withBody("not-json"))); - StopsPredictionsQuery query = StopsPredictionsQuery.builder(List.of("456")).build(); + StopPredictionsQuery query = StopPredictionsQuery.builder(List.of("456")).build(); assertThatThrownBy(() -> this.api.findByStopIds(query)) .isInstanceOf(Cta4jBusException.class) @@ -133,7 +133,7 @@ void findByStopIds_throwsCta4jBusException_whenServerReturnsErrorStatus() { .willReturn(aResponse() .withStatus(500))); - StopsPredictionsQuery query = StopsPredictionsQuery.builder(List.of("456")).build(); + StopPredictionsQuery query = StopPredictionsQuery.builder(List.of("456")).build(); assertThatThrownBy(() -> this.api.findByStopIds(query)) .isInstanceOf(Cta4jBusException.class) @@ -145,7 +145,7 @@ void findByStopIds_throwsCta4jBusException_whenServerReturnsErrorStatus() { @Test void findByVehicleIds_returnsEmpty_whenInputIsEmpty() { - VehiclesPredictionsQuery query = VehiclesPredictionsQuery.builder(List.of()).build(); + VehiclePredictionsQuery query = VehiclePredictionsQuery.builder(List.of()).build(); List predictions = this.api.findByVehicleIds(query); assertThat(predictions).isEmpty(); @@ -161,7 +161,7 @@ void findByVehicleIds_sendsVidParameter() { .withHeader("Content-Type", "application/json") .withBody(TestFixtures.read("bus/prediction/success.json")))); - VehiclesPredictionsQuery query = VehiclesPredictionsQuery.builder(List.of("509")).build(); + VehiclePredictionsQuery query = VehiclePredictionsQuery.builder(List.of("509")).build(); List predictions = this.api.findByVehicleIds(query); assertThat(predictions).hasSize(1); @@ -176,9 +176,9 @@ void findByStopIds_sendsRtParameter_whenRouteIdsProvided() { .withHeader("Content-Type", "application/json") .withBody(TestFixtures.read("bus/prediction/success.json")))); - StopsPredictionsQuery query = StopsPredictionsQuery.builder(List.of("456")) - .routeIds(List.of("8")) - .build(); + StopPredictionsQuery query = StopPredictionsQuery.builder(List.of("456")) + .routeIds(List.of("8")) + .build(); List predictions = this.api.findByStopIds(query); assertThat(predictions).hasSize(1); @@ -193,9 +193,9 @@ void findByStopIds_sendsTopParameter_whenMaxResultsProvided() { .withHeader("Content-Type", "application/json") .withBody(TestFixtures.read("bus/prediction/success.json")))); - StopsPredictionsQuery query = StopsPredictionsQuery.builder(List.of("456")) - .maxResults(5) - .build(); + StopPredictionsQuery query = StopPredictionsQuery.builder(List.of("456")) + .maxResults(5) + .build(); List predictions = this.api.findByStopIds(query); assertThat(predictions).hasSize(1); @@ -253,9 +253,9 @@ void findByVehicleIds_sendsTopParameter_whenMaxResultsProvided() { .withHeader("Content-Type", "application/json") .withBody(TestFixtures.read("bus/prediction/success.json")))); - VehiclesPredictionsQuery query = VehiclesPredictionsQuery.builder(List.of("509")) - .maxResults(3) - .build(); + VehiclePredictionsQuery query = VehiclePredictionsQuery.builder(List.of("509")) + .maxResults(3) + .build(); List predictions = this.api.findByVehicleIds(query); assertThat(predictions).hasSize(1); diff --git a/src/test/java/com/cta4j/bus/prediction/query/StopsPredictionsQueryTest.java b/src/test/java/com/cta4j/bus/prediction/query/StopPredictionsQueryTest.java similarity index 65% rename from src/test/java/com/cta4j/bus/prediction/query/StopsPredictionsQueryTest.java rename to src/test/java/com/cta4j/bus/prediction/query/StopPredictionsQueryTest.java index 87df1550..0f555758 100644 --- a/src/test/java/com/cta4j/bus/prediction/query/StopsPredictionsQueryTest.java +++ b/src/test/java/com/cta4j/bus/prediction/query/StopPredictionsQueryTest.java @@ -7,13 +7,13 @@ import static org.assertj.core.api.Assertions.*; -class StopsPredictionsQueryTest { +class StopPredictionsQueryTest { @Test void builder_buildsQueryWithOptionalParams() { - StopsPredictionsQuery query = StopsPredictionsQuery.builder(List.of("1001")) - .routeIds(List.of("22", "36")) - .maxResults(5) - .build(); + StopPredictionsQuery query = StopPredictionsQuery.builder(List.of("1001")) + .routeIds(List.of("22", "36")) + .maxResults(5) + .build(); assertThat(query.stopIds()).containsExactly("1001"); assertThat(query.routeIds()).containsExactly("22", "36"); @@ -22,7 +22,7 @@ void builder_buildsQueryWithOptionalParams() { @Test void builder_buildsQueryWithNoOptionalParams() { - StopsPredictionsQuery query = StopsPredictionsQuery.builder(List.of("1001")).build(); + StopPredictionsQuery query = StopPredictionsQuery.builder(List.of("1001")).build(); assertThat(query.stopIds()).containsExactly("1001"); assertThat(query.routeIds()).isNull(); @@ -32,13 +32,13 @@ void builder_buildsQueryWithNoOptionalParams() { @Test void builder_throwsIllegalArgumentException_whenMaxResultsIsZero() { assertThatIllegalArgumentException().isThrownBy(() -> - StopsPredictionsQuery.builder(List.of("1001")).maxResults(0)); + StopPredictionsQuery.builder(List.of("1001")).maxResults(0)); } @Test void builder_throwsIllegalArgumentException_whenMaxResultsIsNegative() { assertThatIllegalArgumentException().isThrownBy(() -> - StopsPredictionsQuery.builder(List.of("1001")).maxResults(-1)); + StopPredictionsQuery.builder(List.of("1001")).maxResults(-1)); } @Test @@ -46,12 +46,12 @@ void constructor_throwsIllegalArgumentException_whenTooManyStopIds() { List ids = Collections.nCopies(11, "1001"); assertThatIllegalArgumentException().isThrownBy(() -> - new StopsPredictionsQuery(ids, null, null)); + new StopPredictionsQuery(ids, null, null)); } @Test void constructor_throwsIllegalArgumentException_whenMaxResultsIsNotPositive() { assertThatIllegalArgumentException().isThrownBy(() -> - new StopsPredictionsQuery(List.of("1001"), null, 0)); + new StopPredictionsQuery(List.of("1001"), null, 0)); } } diff --git a/src/test/java/com/cta4j/bus/prediction/query/VehiclesPredictionsQueryTest.java b/src/test/java/com/cta4j/bus/prediction/query/VehiclePredictionsQueryTest.java similarity index 65% rename from src/test/java/com/cta4j/bus/prediction/query/VehiclesPredictionsQueryTest.java rename to src/test/java/com/cta4j/bus/prediction/query/VehiclePredictionsQueryTest.java index 888ce761..bf3ea790 100644 --- a/src/test/java/com/cta4j/bus/prediction/query/VehiclesPredictionsQueryTest.java +++ b/src/test/java/com/cta4j/bus/prediction/query/VehiclePredictionsQueryTest.java @@ -7,12 +7,12 @@ import static org.assertj.core.api.Assertions.*; -class VehiclesPredictionsQueryTest { +class VehiclePredictionsQueryTest { @Test void builder_buildsQueryWithMaxResults() { - VehiclesPredictionsQuery query = VehiclesPredictionsQuery.builder(List.of("509")) - .maxResults(3) - .build(); + VehiclePredictionsQuery query = VehiclePredictionsQuery.builder(List.of("509")) + .maxResults(3) + .build(); assertThat(query.vehicleIds()).containsExactly("509"); assertThat(query.maxResults()).isEqualTo(3); @@ -20,7 +20,7 @@ void builder_buildsQueryWithMaxResults() { @Test void builder_buildsQueryWithNoOptionalParams() { - VehiclesPredictionsQuery query = VehiclesPredictionsQuery.builder(List.of("509")).build(); + VehiclePredictionsQuery query = VehiclePredictionsQuery.builder(List.of("509")).build(); assertThat(query.vehicleIds()).containsExactly("509"); assertThat(query.maxResults()).isNull(); @@ -29,13 +29,13 @@ void builder_buildsQueryWithNoOptionalParams() { @Test void builder_throwsIllegalArgumentException_whenMaxResultsIsZero() { assertThatIllegalArgumentException().isThrownBy(() -> - VehiclesPredictionsQuery.builder(List.of("509")).maxResults(0)); + VehiclePredictionsQuery.builder(List.of("509")).maxResults(0)); } @Test void builder_throwsIllegalArgumentException_whenMaxResultsIsNegative() { assertThatIllegalArgumentException().isThrownBy(() -> - VehiclesPredictionsQuery.builder(List.of("509")).maxResults(-1)); + VehiclePredictionsQuery.builder(List.of("509")).maxResults(-1)); } @Test @@ -43,12 +43,12 @@ void constructor_throwsIllegalArgumentException_whenTooManyVehicleIds() { List ids = Collections.nCopies(11, "509"); assertThatIllegalArgumentException().isThrownBy(() -> - new VehiclesPredictionsQuery(ids, null)); + new VehiclePredictionsQuery(ids, null)); } @Test void constructor_throwsIllegalArgumentException_whenMaxResultsIsNotPositive() { assertThatIllegalArgumentException().isThrownBy(() -> - new VehiclesPredictionsQuery(List.of("509"), 0)); + new VehiclePredictionsQuery(List.of("509"), 0)); } } diff --git a/src/test/java/com/cta4j/train/arrival/ArrivalsApiImplTest.java b/src/test/java/com/cta4j/train/arrival/ArrivalsApiImplTest.java index 25692a66..313d845e 100644 --- a/src/test/java/com/cta4j/train/arrival/ArrivalsApiImplTest.java +++ b/src/test/java/com/cta4j/train/arrival/ArrivalsApiImplTest.java @@ -4,8 +4,8 @@ import com.cta4j.train.arrival.exception.ArrivalsErrorCode; import com.cta4j.train.arrival.exception.Cta4jArrivalsException; import com.cta4j.train.arrival.internal.impl.ArrivalsApiImpl; -import com.cta4j.train.arrival.query.MapArrivalQuery; -import com.cta4j.train.arrival.query.StopArrivalQuery; +import com.cta4j.train.arrival.query.MapArrivalsQuery; +import com.cta4j.train.arrival.query.StopArrivalsQuery; import com.cta4j.train.common.internal.config.TrainApiConfig; import com.cta4j.train.common.model.Arrival; import com.cta4j.common.train.TrainLine; @@ -52,7 +52,7 @@ void findByMapId_returnsArrivals_whenResponseContainsArrivals() { .withHeader("Content-Type", "application/json") .withBody(TestFixtures.read("train/arrival/success.json")))); - MapArrivalQuery query = MapArrivalQuery.builder("40900").build(); + MapArrivalsQuery query = MapArrivalsQuery.builder("40900").build(); List arrivals = this.api.findByMapId(query); assertThat(arrivals).hasSize(1); @@ -72,7 +72,7 @@ void findByMapId_returnsEmpty_whenResponseHasNoEta() { .withHeader("Content-Type", "application/json") .withBody(TestFixtures.read("train/arrival/empty.json")))); - MapArrivalQuery query = MapArrivalQuery.builder("40900").build(); + MapArrivalsQuery query = MapArrivalsQuery.builder("40900").build(); List arrivals = this.api.findByMapId(query); assertThat(arrivals).isEmpty(); @@ -86,7 +86,7 @@ void findByMapId_returnsEmpty_whenEtaIsEmptyArray() { .withHeader("Content-Type", "application/json") .withBody("{\"ctatt\":{\"tmst\":\"2015-04-30T20:23:53\",\"errCd\":\"0\",\"errNm\":null,\"eta\":[]}}"))); - MapArrivalQuery query = MapArrivalQuery.builder("40900").build(); + MapArrivalsQuery query = MapArrivalsQuery.builder("40900").build(); List arrivals = this.api.findByMapId(query); assertThat(arrivals).isEmpty(); @@ -100,7 +100,7 @@ void findByMapId_throwsCta4jArrivalsException_whenResponseContainsError() { .withHeader("Content-Type", "application/json") .withBody(TestFixtures.read("train/arrival/error.json")))); - MapArrivalQuery query = MapArrivalQuery.builder("40900").build(); + MapArrivalsQuery query = MapArrivalsQuery.builder("40900").build(); assertThatThrownBy(() -> this.api.findByMapId(query)) .isInstanceOf(Cta4jArrivalsException.class) @@ -118,7 +118,7 @@ void findByMapId_returnsEmpty_whenResponseContainsInvalidMapIdError() { .withHeader("Content-Type", "application/json") .withBody(TestFixtures.read("train/arrival/not_found_mapid.json")))); - MapArrivalQuery query = MapArrivalQuery.builder("99999").build(); + MapArrivalsQuery query = MapArrivalsQuery.builder("99999").build(); List arrivals = this.api.findByMapId(query); assertThat(arrivals).isEmpty(); @@ -132,7 +132,7 @@ void findByMapId_throwsCta4jArrivalsException_whenResponseIsNotJson() { .withHeader("Content-Type", "application/json") .withBody("not-json"))); - MapArrivalQuery query = MapArrivalQuery.builder("40900").build(); + MapArrivalsQuery query = MapArrivalsQuery.builder("40900").build(); assertThatThrownBy(() -> this.api.findByMapId(query)) .isInstanceOf(Cta4jArrivalsException.class) @@ -150,7 +150,7 @@ void findByStopId_returnsArrivals_whenResponseContainsArrivals() { .withHeader("Content-Type", "application/json") .withBody(TestFixtures.read("train/arrival/success.json")))); - StopArrivalQuery query = StopArrivalQuery.builder("30070").build(); + StopArrivalsQuery query = StopArrivalsQuery.builder("30070").build(); List arrivals = this.api.findByStopId(query); assertThat(arrivals).hasSize(1); @@ -164,7 +164,7 @@ void findByStopId_returnsEmpty_whenResponseHasNoEta() { .withHeader("Content-Type", "application/json") .withBody(TestFixtures.read("train/arrival/empty.json")))); - StopArrivalQuery query = StopArrivalQuery.builder("30070").build(); + StopArrivalsQuery query = StopArrivalsQuery.builder("30070").build(); List arrivals = this.api.findByStopId(query); assertThat(arrivals).isEmpty(); @@ -178,7 +178,7 @@ void findByStopId_throwsCta4jArrivalsException_whenResponseContainsError() { .withHeader("Content-Type", "application/json") .withBody(TestFixtures.read("train/arrival/error.json")))); - StopArrivalQuery query = StopArrivalQuery.builder("30070").build(); + StopArrivalsQuery query = StopArrivalsQuery.builder("30070").build(); assertThatThrownBy(() -> this.api.findByStopId(query)) .isInstanceOf(Cta4jArrivalsException.class) @@ -196,7 +196,7 @@ void findByStopId_returnsEmpty_whenResponseContainsInvalidStopIdError() { .withHeader("Content-Type", "application/json") .withBody(TestFixtures.read("train/arrival/not_found_stpid.json")))); - StopArrivalQuery query = StopArrivalQuery.builder("99999").build(); + StopArrivalsQuery query = StopArrivalsQuery.builder("99999").build(); List arrivals = this.api.findByStopId(query); assertThat(arrivals).isEmpty(); @@ -212,10 +212,10 @@ void findByMapId_sendsLineAndMaxResultsQueryParams_whenSet() { .withHeader("Content-Type", "application/json") .withBody(TestFixtures.read("train/arrival/success.json")))); - MapArrivalQuery query = MapArrivalQuery.builder("40900") - .line(TrainLine.RED) - .maxResults(5) - .build(); + MapArrivalsQuery query = MapArrivalsQuery.builder("40900") + .line(TrainLine.RED) + .maxResults(5) + .build(); List arrivals = this.api.findByMapId(query); @@ -233,10 +233,10 @@ void findByStopId_sendsLineAndMaxResultsQueryParams_whenSet() { .withHeader("Content-Type", "application/json") .withBody(TestFixtures.read("train/arrival/success.json")))); - StopArrivalQuery query = StopArrivalQuery.builder("30070") - .line(TrainLine.RED) - .maxResults(5) - .build(); + StopArrivalsQuery query = StopArrivalsQuery.builder("30070") + .line(TrainLine.RED) + .maxResults(5) + .build(); List arrivals = this.api.findByStopId(query); @@ -278,7 +278,7 @@ void findByMapId_throwsCta4jArrivalsException_whenErrCdIsNotNumeric() { .withHeader("Content-Type", "application/json") .withBody(TestFixtures.read("train/arrival/invalid_err_cd.json")))); - MapArrivalQuery query = MapArrivalQuery.builder("40900").build(); + MapArrivalsQuery query = MapArrivalsQuery.builder("40900").build(); assertThatThrownBy(() -> this.api.findByMapId(query)) .isInstanceOf(Cta4jArrivalsException.class) @@ -295,7 +295,7 @@ void findByMapId_throwsCta4jArrivalsException_whenErrCdIsNegative() { .withHeader("Content-Type", "application/json") .withBody("{\"ctatt\":{\"tmst\":\"2015-04-30T20:23:53\",\"errCd\":\"-1\",\"errNm\":\"Unexpected error\"}}"))); - MapArrivalQuery query = MapArrivalQuery.builder("40900").build(); + MapArrivalsQuery query = MapArrivalsQuery.builder("40900").build(); assertThatThrownBy(() -> this.api.findByMapId(query)) .isInstanceOf(Cta4jArrivalsException.class) @@ -313,7 +313,7 @@ void findByMapId_throwsCta4jArrivalsException_withDefaultMessage_whenErrNmIsBlan .withHeader("Content-Type", "application/json") .withBody("{\"ctatt\":{\"tmst\":\"2015-04-30T20:23:53\",\"errCd\":\"1\",\"errNm\":\"\"}}"))); - MapArrivalQuery query = MapArrivalQuery.builder("40900").build(); + MapArrivalsQuery query = MapArrivalsQuery.builder("40900").build(); assertThatThrownBy(() -> this.api.findByMapId(query)) .isInstanceOf(Cta4jArrivalsException.class) @@ -329,7 +329,7 @@ void findByMapId_throwsCta4jArrivalsException_whenServerReturnsErrorStatus() { .willReturn(aResponse() .withStatus(500))); - MapArrivalQuery query = MapArrivalQuery.builder("40900").build(); + MapArrivalsQuery query = MapArrivalsQuery.builder("40900").build(); assertThatThrownBy(() -> this.api.findByMapId(query)) .isInstanceOf(Cta4jArrivalsException.class) diff --git a/src/test/java/com/cta4j/train/arrival/query/MapArrivalQueryTest.java b/src/test/java/com/cta4j/train/arrival/query/MapArrivalsQueryTest.java similarity index 66% rename from src/test/java/com/cta4j/train/arrival/query/MapArrivalQueryTest.java rename to src/test/java/com/cta4j/train/arrival/query/MapArrivalsQueryTest.java index f6caf655..7b8e572d 100644 --- a/src/test/java/com/cta4j/train/arrival/query/MapArrivalQueryTest.java +++ b/src/test/java/com/cta4j/train/arrival/query/MapArrivalsQueryTest.java @@ -5,13 +5,13 @@ import static org.assertj.core.api.Assertions.*; -class MapArrivalQueryTest { +class MapArrivalsQueryTest { @Test void builder_buildsQueryWithOptionalParams() { - MapArrivalQuery query = MapArrivalQuery.builder("40900") - .line(TrainLine.RED) - .maxResults(5) - .build(); + MapArrivalsQuery query = MapArrivalsQuery.builder("40900") + .line(TrainLine.RED) + .maxResults(5) + .build(); assertThat(query.mapId()).isEqualTo("40900"); assertThat(query.line()).isEqualTo(TrainLine.RED); @@ -20,7 +20,7 @@ void builder_buildsQueryWithOptionalParams() { @Test void builder_buildsQueryWithNoOptionalParams() { - MapArrivalQuery query = MapArrivalQuery.builder("40900").build(); + MapArrivalsQuery query = MapArrivalsQuery.builder("40900").build(); assertThat(query.mapId()).isEqualTo("40900"); assertThat(query.line()).isNull(); @@ -30,18 +30,18 @@ void builder_buildsQueryWithNoOptionalParams() { @Test void builder_throwsIllegalArgumentException_whenMaxResultsIsZero() { assertThatIllegalArgumentException().isThrownBy(() -> - MapArrivalQuery.builder("40900").maxResults(0)); + MapArrivalsQuery.builder("40900").maxResults(0)); } @Test void builder_throwsIllegalArgumentException_whenMaxResultsIsNegative() { assertThatIllegalArgumentException().isThrownBy(() -> - MapArrivalQuery.builder("40900").maxResults(-1)); + MapArrivalsQuery.builder("40900").maxResults(-1)); } @Test void constructor_throwsIllegalArgumentException_whenMaxResultsIsNotPositive() { assertThatIllegalArgumentException().isThrownBy(() -> - new MapArrivalQuery("40900", null, 0)); + new MapArrivalsQuery("40900", null, 0)); } } diff --git a/src/test/java/com/cta4j/train/arrival/query/StopArrivalQueryTest.java b/src/test/java/com/cta4j/train/arrival/query/StopArrivalsQueryTest.java similarity index 66% rename from src/test/java/com/cta4j/train/arrival/query/StopArrivalQueryTest.java rename to src/test/java/com/cta4j/train/arrival/query/StopArrivalsQueryTest.java index 2004c60e..18037432 100644 --- a/src/test/java/com/cta4j/train/arrival/query/StopArrivalQueryTest.java +++ b/src/test/java/com/cta4j/train/arrival/query/StopArrivalsQueryTest.java @@ -5,13 +5,13 @@ import static org.assertj.core.api.Assertions.*; -class StopArrivalQueryTest { +class StopArrivalsQueryTest { @Test void builder_buildsQueryWithOptionalParams() { - StopArrivalQuery query = StopArrivalQuery.builder("30070") - .line(TrainLine.RED) - .maxResults(5) - .build(); + StopArrivalsQuery query = StopArrivalsQuery.builder("30070") + .line(TrainLine.RED) + .maxResults(5) + .build(); assertThat(query.stopId()).isEqualTo("30070"); assertThat(query.line()).isEqualTo(TrainLine.RED); @@ -20,7 +20,7 @@ void builder_buildsQueryWithOptionalParams() { @Test void builder_buildsQueryWithNoOptionalParams() { - StopArrivalQuery query = StopArrivalQuery.builder("30070").build(); + StopArrivalsQuery query = StopArrivalsQuery.builder("30070").build(); assertThat(query.stopId()).isEqualTo("30070"); assertThat(query.line()).isNull(); @@ -30,18 +30,18 @@ void builder_buildsQueryWithNoOptionalParams() { @Test void builder_throwsIllegalArgumentException_whenMaxResultsIsZero() { assertThatIllegalArgumentException().isThrownBy(() -> - StopArrivalQuery.builder("30070").maxResults(0)); + StopArrivalsQuery.builder("30070").maxResults(0)); } @Test void builder_throwsIllegalArgumentException_whenMaxResultsIsNegative() { assertThatIllegalArgumentException().isThrownBy(() -> - StopArrivalQuery.builder("30070").maxResults(-1)); + StopArrivalsQuery.builder("30070").maxResults(-1)); } @Test void constructor_throwsIllegalArgumentException_whenMaxResultsIsNotPositive() { assertThatIllegalArgumentException().isThrownBy(() -> - new StopArrivalQuery("30070", null, 0)); + new StopArrivalsQuery("30070", null, 0)); } } From ccfed4b9a685e98f882631d088517230d75ea8c8 Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Wed, 22 Jul 2026 15:23:13 -0500 Subject: [PATCH 24/60] API interface cleanup/refactor --- .../cta4j/bus/prediction/PredictionsApi.java | 83 ++++++++++++++----- .../java/com/cta4j/bus/stop/StopsApi.java | 24 +++--- .../com/cta4j/train/arrival/ArrivalsApi.java | 22 ++--- .../cta4j/train/location/LocationsApi.java | 22 ++--- .../train/location/LocationsApiImplTest.java | 4 +- 5 files changed, 99 insertions(+), 56 deletions(-) diff --git a/src/main/java/com/cta4j/bus/prediction/PredictionsApi.java b/src/main/java/com/cta4j/bus/prediction/PredictionsApi.java index 54d802b6..a8f8abc9 100644 --- a/src/main/java/com/cta4j/bus/prediction/PredictionsApi.java +++ b/src/main/java/com/cta4j/bus/prediction/PredictionsApi.java @@ -6,6 +6,7 @@ import com.cta4j.bus.prediction.query.VehiclePredictionsQuery; import org.jspecify.annotations.NullMarked; +import java.util.Collection; import java.util.List; import java.util.Objects; @@ -28,15 +29,25 @@ public interface PredictionsApi { List findByStopIds(StopPredictionsQuery query); /** - * Retrieves predictions by vehicle IDs. + * Retrieves predictions by stop IDs. * - * @param query the query parameters for fetching predictions by vehicle IDs - * @return a {@link List} of {@link Prediction}s corresponding to the provided vehicle IDs, or an empty - * {@link List} if no predictions are found - * @throws NullPointerException if {@code query} is {@code null} + * @param stopIds a {@link Collection} of stop IDs + * @return a {@link List} of {@link Prediction}s corresponding to the provided stop IDs, or an empty {@link List} + * if no predictions are found + * @throws NullPointerException if {@code stopIds} is {@code null}, or if any element of {@code stopIds} is + * {@code null} * @throws Cta4jBusException if the API returns an error response or the response cannot be parsed */ - List findByVehicleIds(VehiclePredictionsQuery query); + default List findByStopIds(Collection stopIds) { + Objects.requireNonNull(stopIds); + + List stopIdsList = List.copyOf(stopIds); + + StopPredictionsQuery query = StopPredictionsQuery.builder(stopIdsList) + .build(); + + return this.findByStopIds(query); + } /** * Retrieves predictions by stop ID. @@ -59,27 +70,35 @@ default List findByStopId(String stopId) { } /** - * Retrieves predictions by route ID and stop ID. + * Retrieves predictions by vehicle IDs. * - * @param routeId the route ID - * @param stopId the stop ID - * @return a {@link List} of {@link Prediction}s corresponding to the provided route ID and stop ID, or an empty + * @param query the query parameters for fetching predictions by vehicle IDs + * @return a {@link List} of {@link Prediction}s corresponding to the provided vehicle IDs, or an empty * {@link List} if no predictions are found - * @throws NullPointerException if {@code routeId} or {@code stopId} is {@code null} + * @throws NullPointerException if {@code query} is {@code null} * @throws Cta4jBusException if the API returns an error response or the response cannot be parsed */ - default List findByRouteIdAndStopId(String routeId, String stopId) { - Objects.requireNonNull(routeId); - Objects.requireNonNull(stopId); + List findByVehicleIds(VehiclePredictionsQuery query); - List stopIds = List.of(stopId); - List routeIds = List.of(routeId); + /** + * Retrieves predictions by vehicle IDs. + * + * @param vehicleIds a {@link Collection} of vehicle IDs + * @return a {@link List} of {@link Prediction}s corresponding to the provided vehicle IDs, or an empty + * {@link List} if no predictions are found + * @throws NullPointerException if {@code vehicleIds} is {@code null}, or if any element of {@code vehicleIds} + * is {@code null} + * @throws Cta4jBusException if the API returns an error response or the response cannot be parsed + */ + default List findByVehicleIds(Collection vehicleIds) { + Objects.requireNonNull(vehicleIds); - StopPredictionsQuery query = StopPredictionsQuery.builder(stopIds) - .routeIds(routeIds) - .build(); + List vehicleIdsList = List.copyOf(vehicleIds); - return this.findByStopIds(query); + VehiclePredictionsQuery query = VehiclePredictionsQuery.builder(vehicleIdsList) + .build(); + + return this.findByVehicleIds(query); } /** @@ -101,4 +120,28 @@ default List findByVehicleId(String vehicleId) { return this.findByVehicleIds(query); } + + /** + * Retrieves predictions by route ID and stop ID. + * + * @param routeId the route ID + * @param stopId the stop ID + * @return a {@link List} of {@link Prediction}s corresponding to the provided route ID and stop ID, or an empty + * {@link List} if no predictions are found + * @throws NullPointerException if {@code routeId} or {@code stopId} is {@code null} + * @throws Cta4jBusException if the API returns an error response or the response cannot be parsed + */ + default List findByRouteIdAndStopId(String routeId, String stopId) { + Objects.requireNonNull(routeId); + Objects.requireNonNull(stopId); + + List stopIds = List.of(stopId); + List routeIds = List.of(routeId); + + StopPredictionsQuery query = StopPredictionsQuery.builder(stopIds) + .routeIds(routeIds) + .build(); + + return this.findByStopIds(query); + } } diff --git a/src/main/java/com/cta4j/bus/stop/StopsApi.java b/src/main/java/com/cta4j/bus/stop/StopsApi.java index 79f992f5..8ece0856 100644 --- a/src/main/java/com/cta4j/bus/stop/StopsApi.java +++ b/src/main/java/com/cta4j/bus/stop/StopsApi.java @@ -17,18 +17,6 @@ */ @NullMarked public interface StopsApi { - /** - * Retrieves stops by route ID and direction. - * - * @param routeId the route ID - * @param direction the direction (e.g., "Northbound", "Southbound") - * @return a {@link List} of {@link Stop}s corresponding to the provided route ID and direction, or an empty - * {@link List} if no stops are found - * @throws NullPointerException if {@code routeId} or {@code direction} is {@code null} - * @throws Cta4jBusException if the API returns an error response or the response cannot be parsed - */ - List findByRouteIdAndDirection(String routeId, String direction); - /** * Retrieves stops by their IDs. * @@ -72,4 +60,16 @@ default Optional findById(String stopId) { return Optional.of(stop); } + + /** + * Retrieves stops by route ID and direction. + * + * @param routeId the route ID + * @param direction the direction (e.g., "Northbound", "Southbound") + * @return a {@link List} of {@link Stop}s corresponding to the provided route ID and direction, or an empty + * {@link List} if no stops are found + * @throws NullPointerException if {@code routeId} or {@code direction} is {@code null} + * @throws Cta4jBusException if the API returns an error response or the response cannot be parsed + */ + List findByRouteIdAndDirection(String routeId, String direction); } diff --git a/src/main/java/com/cta4j/train/arrival/ArrivalsApi.java b/src/main/java/com/cta4j/train/arrival/ArrivalsApi.java index c384a2a0..e972855d 100644 --- a/src/main/java/com/cta4j/train/arrival/ArrivalsApi.java +++ b/src/main/java/com/cta4j/train/arrival/ArrivalsApi.java @@ -26,17 +26,6 @@ public interface ArrivalsApi { */ List findByMapId(MapArrivalsQuery query); - /** - * Retrieves arrivals by stop ID. - * - * @param query the query parameters for fetching arrivals by stop ID - * @return a {@link List} of {@link Arrival}s corresponding to the provided stop ID, or an empty {@link List} if no - * arrivals are found - * @throws NullPointerException if {@code query} is {@code null} - * @throws Cta4jArrivalsException if the API returns an error response or the response cannot be parsed - */ - List findByStopId(StopArrivalsQuery query); - /** * Retrieves arrivals by map ID. * @@ -53,6 +42,17 @@ default List findByMapId(String mapId) { return this.findByMapId(query); } + /** + * Retrieves arrivals by stop ID. + * + * @param query the query parameters for fetching arrivals by stop ID + * @return a {@link List} of {@link Arrival}s corresponding to the provided stop ID, or an empty {@link List} if no + * arrivals are found + * @throws NullPointerException if {@code query} is {@code null} + * @throws Cta4jArrivalsException if the API returns an error response or the response cannot be parsed + */ + List findByStopId(StopArrivalsQuery query); + /** * Retrieves arrivals by stop ID. * diff --git a/src/main/java/com/cta4j/train/location/LocationsApi.java b/src/main/java/com/cta4j/train/location/LocationsApi.java index 934e25e4..c4db0360 100644 --- a/src/main/java/com/cta4j/train/location/LocationsApi.java +++ b/src/main/java/com/cta4j/train/location/LocationsApi.java @@ -14,6 +14,17 @@ */ @NullMarked public interface LocationsApi { + /** + * Retrieves train locations for all lines. + * + * @return a {@link List} of {@link TrainLocations} for all lines, or an empty {@link List} if no train locations + * are found + * @throws Cta4jLocationsException if the API returns an error response or the response cannot be parsed + */ + default List list() { + return findByLines(List.of(TrainLine.values())); + } + /** * Retrieves train locations for the specified lines. * @@ -37,15 +48,4 @@ public interface LocationsApi { default List findByLine(TrainLine line) { return findByLines(List.of(line)); } - - /** - * Retrieves train locations for all lines. - * - * @return a {@link List} of {@link TrainLocations} for all lines, or an empty {@link List} if no train locations - * are found - * @throws Cta4jLocationsException if the API returns an error response or the response cannot be parsed - */ - default List findAll() { - return findByLines(List.of(TrainLine.values())); - } } diff --git a/src/test/java/com/cta4j/train/location/LocationsApiImplTest.java b/src/test/java/com/cta4j/train/location/LocationsApiImplTest.java index ba1d1085..69ad14ac 100644 --- a/src/test/java/com/cta4j/train/location/LocationsApiImplTest.java +++ b/src/test/java/com/cta4j/train/location/LocationsApiImplTest.java @@ -139,14 +139,14 @@ void findByLine_sendsRtParameter() { } @Test - void findAll_returnsLocations_whenResponseContainsData() { + void list_returnsLocations_whenResponseContainsData() { this.server.stubFor(get(urlPathEqualTo("/api/1.0/ttpositions.aspx")) .willReturn(aResponse() .withStatus(200) .withHeader("Content-Type", "application/json") .withBody(TestFixtures.read("train/location/success.json")))); - List locations = this.api.findAll(); + List locations = this.api.list(); assertThat(locations).hasSize(1); } From 830187392e2dd4511412e7480765592fda3dd591 Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Fri, 24 Jul 2026 15:36:17 -0500 Subject: [PATCH 25/60] Add DetailedAlertsApi and custom exception for detailed alerts handling --- src/main/java/com/cta4j/alert/AlertApi.java | 8 ++ .../common/internal/impl/AlertApiImpl.java | 6 + .../cta4j/alert/common/model/ServiceType.java | 2 +- .../detailedalert/DetailedAlertsApi.java | 107 +++++++++++++- .../Cta4jDetailedAlertsException.java | 51 +++++++ .../exception/DetailedAlertsErrorCode.java | 130 ++++++++++++++++++ 6 files changed, 302 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/cta4j/alert/detailedalert/exception/Cta4jDetailedAlertsException.java create mode 100644 src/main/java/com/cta4j/alert/detailedalert/exception/DetailedAlertsErrorCode.java diff --git a/src/main/java/com/cta4j/alert/AlertApi.java b/src/main/java/com/cta4j/alert/AlertApi.java index d8b8dd3c..890817e5 100644 --- a/src/main/java/com/cta4j/alert/AlertApi.java +++ b/src/main/java/com/cta4j/alert/AlertApi.java @@ -1,6 +1,7 @@ package com.cta4j.alert; import com.cta4j.alert.common.internal.impl.AlertApiImpl; +import com.cta4j.alert.detailedalert.DetailedAlertsApi; import com.cta4j.alert.routestatus.RouteStatusApi; import org.jspecify.annotations.NullMarked; @@ -22,6 +23,13 @@ public interface AlertApi { */ RouteStatusApi routeStatus(); + /** + * Provides access to detailed alert-related endpoints. + * + * @return the {@link DetailedAlertsApi} + */ + DetailedAlertsApi detailedAlerts(); + /** * Builder for constructing {@link AlertApi} instances. */ diff --git a/src/main/java/com/cta4j/alert/common/internal/impl/AlertApiImpl.java b/src/main/java/com/cta4j/alert/common/internal/impl/AlertApiImpl.java index a6ed0561..79a618e5 100644 --- a/src/main/java/com/cta4j/alert/common/internal/impl/AlertApiImpl.java +++ b/src/main/java/com/cta4j/alert/common/internal/impl/AlertApiImpl.java @@ -3,6 +3,7 @@ import com.cta4j.alert.AlertApi; import com.cta4j.alert.common.internal.config.AlertApiConfig; import com.cta4j.alert.common.internal.util.AlertApiConstants; +import com.cta4j.alert.detailedalert.DetailedAlertsApi; import com.cta4j.alert.routestatus.RouteStatusApi; import com.cta4j.alert.routestatus.internal.impl.RouteStatusApiImpl; import org.jetbrains.annotations.ApiStatus; @@ -27,6 +28,11 @@ public RouteStatusApi routeStatus() { return this.routeStatusApi; } + @Override + public DetailedAlertsApi detailedAlerts() { + return null; + } + public static final class BuilderImpl implements AlertApi.Builder { @Nullable private String host; diff --git a/src/main/java/com/cta4j/alert/common/model/ServiceType.java b/src/main/java/com/cta4j/alert/common/model/ServiceType.java index 38fb3ffe..bbd1268b 100644 --- a/src/main/java/com/cta4j/alert/common/model/ServiceType.java +++ b/src/main/java/com/cta4j/alert/common/model/ServiceType.java @@ -3,7 +3,7 @@ import org.jspecify.annotations.NullMarked; /** - * Represents a category of CTA service — a bus route, train route, train station, or systemwide grouping. + * Represents a category of CTA service - a bus route, train route, train station, or systemwide grouping. */ @NullMarked public enum ServiceType { diff --git a/src/main/java/com/cta4j/alert/detailedalert/DetailedAlertsApi.java b/src/main/java/com/cta4j/alert/detailedalert/DetailedAlertsApi.java index c0cecc69..0dee0d6a 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/DetailedAlertsApi.java +++ b/src/main/java/com/cta4j/alert/detailedalert/DetailedAlertsApi.java @@ -1,5 +1,6 @@ package com.cta4j.alert.detailedalert; +import com.cta4j.alert.detailedalert.exception.Cta4jDetailedAlertsException; import com.cta4j.alert.detailedalert.model.Alert; import com.cta4j.alert.detailedalert.query.AlertsQuery; import com.cta4j.alert.detailedalert.query.BusRouteAlertsQuery; @@ -12,10 +13,30 @@ import java.util.List; import java.util.Objects; +/** + * Provides access to detailed alert-related endpoints of the CTA Alerts API. + *

+ * This API allows retrieval of all alerts, or filtered by bus route ID, train line, or station ID. + */ @NullMarked public interface DetailedAlertsApi { + /** + * Retrieves alerts matching the given query parameters. + * + * @param query the query parameters for fetching alerts + * @return a {@link List} of {@link Alert}s matching the query, or an empty {@link List} if no alerts are found + * @throws NullPointerException if {@code query} is {@code null} + * @throws Cta4jDetailedAlertsException if the API returns an error response or the response cannot be parsed + */ List list(AlertsQuery query); + /** + * Retrieves alerts using the default query parameters. + * + * @return a {@link List} of {@link Alert}s matching the default query, or an empty {@link List} if no alerts are + * found + * @throws Cta4jDetailedAlertsException if the API returns an error response or the response cannot be parsed + */ default List list() { AlertsQuery query = AlertsQuery.builder() .build(); @@ -23,8 +44,27 @@ default List list() { return this.list(query); } + /** + * Retrieves alerts by bus route IDs. + * + * @param query the query parameters for fetching alerts by bus route IDs + * @return a {@link List} of {@link Alert}s corresponding to the provided bus route IDs, or an empty {@link List} + * if no alerts are found + * @throws NullPointerException if {@code query} is {@code null} + * @throws Cta4jDetailedAlertsException if the API returns an error response or the response cannot be parsed + */ List findByBusRouteIds(BusRouteAlertsQuery query); + /** + * Retrieves alerts by bus route IDs. + * + * @param routeIds a {@link Collection} of bus route IDs + * @return a {@link List} of {@link Alert}s corresponding to the provided bus route IDs, or an empty {@link List} + * if no alerts are found + * @throws NullPointerException if {@code routeIds} is {@code null}, or if any element of {@code routeIds} is + * {@code null} + * @throws Cta4jDetailedAlertsException if the API returns an error response or the response cannot be parsed + */ default List findByBusRouteIds(Collection routeIds) { Objects.requireNonNull(routeIds); @@ -36,6 +76,15 @@ default List findByBusRouteIds(Collection routeIds) { return this.findByBusRouteIds(query); } + /** + * Retrieves alerts by bus route ID. + * + * @param routeId the bus route ID + * @return a {@link List} of {@link Alert}s corresponding to the provided bus route ID, or an empty {@link List} + * if no alerts are found + * @throws NullPointerException if {@code routeId} is {@code null} + * @throws Cta4jDetailedAlertsException if the API returns an error response or the response cannot be parsed + */ default List findByBusRouteId(String routeId) { Objects.requireNonNull(routeId); @@ -44,8 +93,27 @@ default List findByBusRouteId(String routeId) { return this.findByBusRouteIds(routeIds); } + /** + * Retrieves alerts by train lines. + * + * @param query the query parameters for fetching alerts by train lines + * @return a {@link List} of {@link Alert}s corresponding to the provided train lines, or an empty {@link List} + * if no alerts are found + * @throws NullPointerException if {@code query} is {@code null} + * @throws Cta4jDetailedAlertsException if the API returns an error response or the response cannot be parsed + */ List findByLines(LineAlertsQuery query); + /** + * Retrieves alerts by train lines. + * + * @param lines a {@link Collection} of train lines + * @return a {@link List} of {@link Alert}s corresponding to the provided train lines, or an empty {@link List} + * if no alerts are found + * @throws NullPointerException if {@code lines} is {@code null}, or if any element of {@code lines} is + * {@code null} + * @throws Cta4jDetailedAlertsException if the API returns an error response or the response cannot be parsed + */ default List findByLines(Collection lines) { Objects.requireNonNull(lines); @@ -57,6 +125,15 @@ default List findByLines(Collection lines) { return this.findByLines(query); } + /** + * Retrieves alerts by train line. + * + * @param line the train line + * @return a {@link List} of {@link Alert}s corresponding to the provided train line, or an empty {@link List} if + * no alerts are found + * @throws NullPointerException if {@code line} is {@code null} + * @throws Cta4jDetailedAlertsException if the API returns an error response or the response cannot be parsed + */ default List findByLine(TrainLine line) { Objects.requireNonNull(line); @@ -65,8 +142,27 @@ default List findByLine(TrainLine line) { return this.findByLines(lines); } + /** + * Retrieves alerts by station IDs. + * + * @param query the query parameters for fetching alerts by station IDs + * @return a {@link List} of {@link Alert}s corresponding to the provided station IDs, or an empty {@link List} + * if no alerts are found + * @throws NullPointerException if {@code query} is {@code null} + * @throws Cta4jDetailedAlertsException if the API returns an error response or the response cannot be parsed + */ List findByStationIds(StationAlertsQuery query); + /** + * Retrieves alerts by station IDs. + * + * @param stationIds a {@link Collection} of station IDs + * @return a {@link List} of {@link Alert}s corresponding to the provided station IDs, or an empty {@link List} + * if no alerts are found + * @throws NullPointerException if {@code stationIds} is {@code null}, or if any element of {@code stationIds} is + * {@code null} + * @throws Cta4jDetailedAlertsException if the API returns an error response or the response cannot be parsed + */ default List findByStationIds(Collection stationIds) { Objects.requireNonNull(stationIds); @@ -78,6 +174,15 @@ default List findByStationIds(Collection stationIds) { return this.findByStationIds(query); } + /** + * Retrieves alerts by station ID. + * + * @param stationId the station ID + * @return a {@link List} of {@link Alert}s corresponding to the provided station ID, or an empty {@link List} if + * no alerts are found + * @throws NullPointerException if {@code stationId} is {@code null} + * @throws Cta4jDetailedAlertsException if the API returns an error response or the response cannot be parsed + */ default List findByStationId(String stationId) { Objects.requireNonNull(stationId); @@ -85,4 +190,4 @@ default List findByStationId(String stationId) { return this.findByStationIds(stationIds); } -} +} \ No newline at end of file diff --git a/src/main/java/com/cta4j/alert/detailedalert/exception/Cta4jDetailedAlertsException.java b/src/main/java/com/cta4j/alert/detailedalert/exception/Cta4jDetailedAlertsException.java new file mode 100644 index 00000000..d61a2e8d --- /dev/null +++ b/src/main/java/com/cta4j/alert/detailedalert/exception/Cta4jDetailedAlertsException.java @@ -0,0 +1,51 @@ +package com.cta4j.alert.detailedalert.exception; + +import com.cta4j.alert.common.exception.Cta4jAlertException; +import com.cta4j.alert.common.internal.util.AlertApiConstants; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +/** + * A custom exception class for handling cta4j detailed alerts-specific errors. + */ +@NullMarked +public final class Cta4jDetailedAlertsException extends Cta4jAlertException { + /** + * The error code associated with this exception, if available. + */ + @Nullable + private final DetailedAlertsErrorCode errorCode; + + /** + * Constructs a {@code Cta4jDetailedAlertsException}. + * + * @param message the detail message + * @param cause the cause of the exception + */ + public Cta4jDetailedAlertsException(String message, Throwable cause) { + super(message, AlertApiConstants.DETAILED_ALERTS_ENDPOINT, cause); + + this.errorCode = null; + } + + /** + * Constructs a {@code Cta4jDetailedAlertsException}. + * + * @param message the detail message + * @param rawErrorCode the raw error code associated with the exception + */ + public Cta4jDetailedAlertsException(String message, int rawErrorCode) { + super(message, AlertApiConstants.DETAILED_ALERTS_ENDPOINT, rawErrorCode); + + this.errorCode = DetailedAlertsErrorCode.fromCode(rawErrorCode); + } + + /** + * Returns the error code associated with this exception, if available. + * + * @return the error code, or {@code null} if not available + */ + public @Nullable DetailedAlertsErrorCode getErrorCode() { + return this.errorCode; + } +} diff --git a/src/main/java/com/cta4j/alert/detailedalert/exception/DetailedAlertsErrorCode.java b/src/main/java/com/cta4j/alert/detailedalert/exception/DetailedAlertsErrorCode.java new file mode 100644 index 00000000..be9b6849 --- /dev/null +++ b/src/main/java/com/cta4j/alert/detailedalert/exception/DetailedAlertsErrorCode.java @@ -0,0 +1,130 @@ +package com.cta4j.alert.detailedalert.exception; + +import org.jspecify.annotations.NullMarked; + +/** + * Represents the error codes returned by the CTA Detailed Alerts API. + */ +@NullMarked +public enum DetailedAlertsErrorCode { + /** + * Indicates that the request was successful and there were no errors. + */ + OK(0), + + /** + * Indicates that there are no active alerts. + */ + NO_ACTIVE_ALERTS(25), + + /** + * Indicates that there are no active alerts based on the provided filter criteria. + */ + NO_ACTIVE_ALERTS_FOR_FILTER(50), + + /** + * Indicates that the provided "activeonly" value is invalid. + */ + INVALID_ACTIVEONLY(100), + + /** + * Indicates that the provided "accessibility" value is invalid. + */ + INVALID_ACCESSIBILITY(101), + + /** + * Indicates that the provided "planned" value is invalid. + */ + INVALID_PLANNED(102), + + /** + * Indicates that the provided station ID is not an integer. + */ + STATIONID_NOT_INTEGER(103), + + /** + * Indicates that the provided "bystartdate" value is not a valid date in "yyyyMMdd" format. + */ + INVALID_BYSTARTDATE(104), + + /** + * Indicates that the provided "recentdays" value is not an integer. + */ + RECENTDAYS_NOT_INTEGER(105), + + /** + * Indicates that the "routeid" and "stationid" parameters were both provided, which is not allowed. + */ + ROUTEID_STATIONID_CONFLICT(106), + + /** + * Indicates that the "recentdays" and "bystartdate" parameters were both provided, which is not allowed. + */ + RECENTDAYS_BYSTARTDATE_CONFLICT(107), + + /** + * Indicates that the query string contains a parameter that is not recognized by the API. The supported API + * parameters are "activeonly", "accessibility", "planned", "routeid", "stationid", "bystartdate", "recentdays", + * and "outputType". + */ + INVALID_PARAMETER(500), + + /** + * Indicates that the server encountered an unexpected error that prevented it from fulfilling the request. + */ + SERVER_ERROR(900), + + /** + * Indicates that an unknown error occurred that does not match any of the defined error codes. + */ + UNKNOWN(-1); + + /** + * The integer code associated with this error code. + */ + private final int code; + + /** + * Constructs a {@code DetailedAlertsErrorCode}. + * + * @param code the integer code associated with the error code + */ + DetailedAlertsErrorCode(int code) { + this.code = code; + } + + /** + * Returns the integer code associated with this error code. + * + * @return the integer code + */ + public int getCode() { + return this.code; + } + + /** + * Returns the {@code DetailedAlertsErrorCode} corresponding to the given integer code. + * + * @param code the integer code to look up + * @return the corresponding {@code DetailedAlertsErrorCode}, or {@code UNKNOWN} if the code does not match any + * defined error code + */ + public static DetailedAlertsErrorCode fromCode(int code) { + return switch (code) { + case 0 -> OK; + case 25 -> NO_ACTIVE_ALERTS; + case 50 -> NO_ACTIVE_ALERTS_FOR_FILTER; + case 100 -> INVALID_ACTIVEONLY; + case 101 -> INVALID_ACCESSIBILITY; + case 102 -> INVALID_PLANNED; + case 103 -> STATIONID_NOT_INTEGER; + case 104 -> INVALID_BYSTARTDATE; + case 105 -> RECENTDAYS_NOT_INTEGER; + case 106 -> ROUTEID_STATIONID_CONFLICT; + case 107 -> RECENTDAYS_BYSTARTDATE_CONFLICT; + case 500 -> INVALID_PARAMETER; + case 900 -> SERVER_ERROR; + default -> UNKNOWN; + }; + } +} From 65783662cc307681d65719257dde078d267b2a42 Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Sat, 25 Jul 2026 13:45:10 -0500 Subject: [PATCH 26/60] Add AlertMapper, BooleanParser, and TimestampParser utility classes for alert data mapping and parsing --- .../common/internal/mapper/Qualifiers.java | 21 ++++++++ .../internal/mapper/AlertMapper.java | 25 +++++++++ .../common/internal/mapper/Qualifiers.java | 17 +------ .../common/internal/util/BooleanParser.java | 28 ++++++++++ .../common/internal/util/TimestampParser.java | 51 +++++++++++++++++++ .../common/internal/mapper/Qualifiers.java | 24 ++------- 6 files changed, 131 insertions(+), 35 deletions(-) create mode 100644 src/main/java/com/cta4j/alert/detailedalert/internal/mapper/AlertMapper.java create mode 100644 src/main/java/com/cta4j/common/internal/util/BooleanParser.java create mode 100644 src/main/java/com/cta4j/common/internal/util/TimestampParser.java diff --git a/src/main/java/com/cta4j/alert/common/internal/mapper/Qualifiers.java b/src/main/java/com/cta4j/alert/common/internal/mapper/Qualifiers.java index 0e730329..b0e22b62 100644 --- a/src/main/java/com/cta4j/alert/common/internal/mapper/Qualifiers.java +++ b/src/main/java/com/cta4j/alert/common/internal/mapper/Qualifiers.java @@ -1,16 +1,25 @@ package com.cta4j.alert.common.internal.mapper; +import com.cta4j.common.internal.util.BooleanParser; +import com.cta4j.common.internal.util.TimestampParser; import org.jetbrains.annotations.ApiStatus; import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import org.mapstruct.Named; import java.net.URI; import java.net.URISyntaxException; +import java.time.Instant; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; import java.util.Objects; @ApiStatus.Internal @NullMarked public final class Qualifiers { + private static final DateTimeFormatter TIMESTAMP_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"); + private static final ZoneId CHICAGO_ZONE_ID = ZoneId.of("America/Chicago"); + private Qualifiers() { throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); } @@ -27,4 +36,16 @@ public static URI mapUri(String value) { throw new IllegalArgumentException(message, e); } } + + @Named("mapTimestamp") + public static @Nullable Instant mapTimestamp(@Nullable String timestamp) { + return TimestampParser.parseNullable(timestamp, TIMESTAMP_FORMATTER, CHICAGO_ZONE_ID); + } + + @Named("map01ToBoolean") + public static boolean map01ToBoolean(String value) { + Objects.requireNonNull(value); + + return BooleanParser.parse01(value); + } } diff --git a/src/main/java/com/cta4j/alert/detailedalert/internal/mapper/AlertMapper.java b/src/main/java/com/cta4j/alert/detailedalert/internal/mapper/AlertMapper.java new file mode 100644 index 00000000..0d494ce0 --- /dev/null +++ b/src/main/java/com/cta4j/alert/detailedalert/internal/mapper/AlertMapper.java @@ -0,0 +1,25 @@ +package com.cta4j.alert.detailedalert.internal.mapper; + +import com.cta4j.alert.common.internal.mapper.Qualifiers; +import com.cta4j.alert.detailedalert.internal.wire.CtaAlert; +import com.cta4j.alert.detailedalert.model.Alert; +import org.jetbrains.annotations.ApiStatus; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +@Mapper(uses = Qualifiers.class) +@ApiStatus.Internal +public interface AlertMapper { + AlertMapper INSTANCE = Mappers.getMapper(AlertMapper.class); + + @Mapping(target = "id", source = "alertId") + @Mapping(target = "severity.score", source = "severityScore") + @Mapping(target = "severity.color", source = "severityColor") + @Mapping(target = "severity.css", source = "severityCss") + @Mapping(target = "startTime", source = "eventStart", qualifiedByName = "mapTimestamp") + @Mapping(target = "endTime", source = "eventEnd", qualifiedByName = "mapTimestamp") + @Mapping(target = "openEnded", source = "tbd", qualifiedByName = "map01ToBoolean") + //todo: finish mapping + Alert toDomain(CtaAlert alert); +} diff --git a/src/main/java/com/cta4j/bus/common/internal/mapper/Qualifiers.java b/src/main/java/com/cta4j/bus/common/internal/mapper/Qualifiers.java index eb72d01e..1d1c0b11 100644 --- a/src/main/java/com/cta4j/bus/common/internal/mapper/Qualifiers.java +++ b/src/main/java/com/cta4j/bus/common/internal/mapper/Qualifiers.java @@ -6,16 +6,15 @@ import com.cta4j.bus.prediction.model.PassengerLoad; import com.cta4j.bus.prediction.model.PredictionType; import com.cta4j.bus.vehicle.model.TransitMode; +import com.cta4j.common.internal.util.TimestampParser; import org.jetbrains.annotations.ApiStatus; import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.mapstruct.Named; import java.time.Instant; -import java.time.LocalDateTime; import java.time.ZoneId; import java.time.format.DateTimeFormatter; -import java.time.format.DateTimeParseException; import java.util.Locale; import java.util.Objects; @@ -46,19 +45,7 @@ public static PredictionType mapPredictionType(String typ) { @Named("mapTimestamp") public static @Nullable Instant mapTimestamp(@Nullable String timestamp) { - if (timestamp == null) { - return null; - } - - try { - return LocalDateTime.parse(timestamp, TIMESTAMP_FORMATTER) - .atZone(CHICAGO_ZONE_ID) - .toInstant(); - } catch (DateTimeParseException e) { - String message = "Failed to parse timestamp: %s".formatted(timestamp); - - throw new IllegalArgumentException(message, e); - } + return TimestampParser.parseNullable(timestamp, TIMESTAMP_FORMATTER, CHICAGO_ZONE_ID); } @Named("mapDynamicAction") diff --git a/src/main/java/com/cta4j/common/internal/util/BooleanParser.java b/src/main/java/com/cta4j/common/internal/util/BooleanParser.java new file mode 100644 index 00000000..742534d5 --- /dev/null +++ b/src/main/java/com/cta4j/common/internal/util/BooleanParser.java @@ -0,0 +1,28 @@ +package com.cta4j.common.internal.util; + +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; + +import java.util.Objects; + +@ApiStatus.Internal +@NullMarked +public final class BooleanParser { + private BooleanParser() { + throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); + } + + public static boolean parse01(String value) { + Objects.requireNonNull(value); + + return switch (value) { + case "0" -> false; + case "1" -> true; + default -> { + String message = "Invalid value: %s. Expected 0 or 1".formatted(value); + + throw new IllegalArgumentException(message); + } + }; + } +} diff --git a/src/main/java/com/cta4j/common/internal/util/TimestampParser.java b/src/main/java/com/cta4j/common/internal/util/TimestampParser.java new file mode 100644 index 00000000..c4ca5508 --- /dev/null +++ b/src/main/java/com/cta4j/common/internal/util/TimestampParser.java @@ -0,0 +1,51 @@ +package com.cta4j.common.internal.util; + +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.util.Objects; + +@ApiStatus.Internal +@NullMarked +public final class TimestampParser { + private TimestampParser() { + throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); + } + + public static Instant parse(String timestamp, DateTimeFormatter formatter, ZoneId zoneId) { + Objects.requireNonNull(timestamp); + Objects.requireNonNull(formatter); + Objects.requireNonNull(zoneId); + + try { + return LocalDateTime.parse(timestamp, formatter) + .atZone(zoneId) + .toInstant(); + } catch (DateTimeParseException e) { + String message = "Failed to parse timestamp: %s".formatted(timestamp); + + throw new IllegalArgumentException(message, e); + } + } + + public static @Nullable Instant parseNullable( + @Nullable String timestamp, + DateTimeFormatter formatter, + ZoneId zoneId + ) { + Objects.requireNonNull(formatter); + Objects.requireNonNull(zoneId); + + if (timestamp == null) { + return null; + } + + return parse(timestamp, formatter, zoneId); + } +} diff --git a/src/main/java/com/cta4j/train/common/internal/mapper/Qualifiers.java b/src/main/java/com/cta4j/train/common/internal/mapper/Qualifiers.java index d42a3856..a5c59f15 100644 --- a/src/main/java/com/cta4j/train/common/internal/mapper/Qualifiers.java +++ b/src/main/java/com/cta4j/train/common/internal/mapper/Qualifiers.java @@ -1,6 +1,8 @@ package com.cta4j.train.common.internal.mapper; import com.cta4j.common.geo.Coordinates; +import com.cta4j.common.internal.util.BooleanParser; +import com.cta4j.common.internal.util.TimestampParser; import com.cta4j.train.common.internal.wire.CtaArrival; import com.cta4j.train.common.model.TrainDirection; import com.cta4j.common.train.TrainLine; @@ -17,10 +19,8 @@ import java.math.BigDecimal; import java.time.Instant; -import java.time.LocalDateTime; import java.time.ZoneId; import java.time.format.DateTimeFormatter; -import java.time.format.DateTimeParseException; import java.util.EnumSet; import java.util.Objects; import java.util.Set; @@ -114,30 +114,14 @@ public static TrainLine mapLine(String line) { public static Instant mapTimestamp(String timestamp) { Objects.requireNonNull(timestamp); - try { - return LocalDateTime.parse(timestamp, TIMESTAMP_FORMATTER) - .atZone(CHICAGO_ZONE_ID) - .toInstant(); - } catch (DateTimeParseException e) { - String message = "Failed to parse timestamp: %s".formatted(timestamp); - - throw new IllegalArgumentException(message, e); - } + return TimestampParser.parse(timestamp, TIMESTAMP_FORMATTER, CHICAGO_ZONE_ID); } @Named("map01ToBoolean") public static boolean map01ToBoolean(String value) { Objects.requireNonNull(value); - return switch (value) { - case "0" -> false; - case "1" -> true; - default -> { - String message = "Invalid boolean value: %s. Expected 0 or 1".formatted(value); - - throw new IllegalArgumentException(message); - } - }; + return BooleanParser.parse01(value); } @Named("map15ToTrainDirection") From 03f59030a1f3cbd0a59c3177442b1e1d6b2b7a07 Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Sun, 26 Jul 2026 13:45:52 -0500 Subject: [PATCH 27/60] Add ttim and guid fields to Alert and ImpactedService models; update AlertMapper for new fields --- .../common/internal/mapper/Qualifiers.java | 18 ++++++++++++++++++ .../internal/mapper/AlertMapper.java | 17 ++++++++++++++++- .../cta4j/alert/detailedalert/model/Alert.java | 12 +++++++++++- .../detailedalert/model/ImpactedService.java | 8 ++++++-- 4 files changed, 51 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/cta4j/alert/common/internal/mapper/Qualifiers.java b/src/main/java/com/cta4j/alert/common/internal/mapper/Qualifiers.java index b0e22b62..e8783690 100644 --- a/src/main/java/com/cta4j/alert/common/internal/mapper/Qualifiers.java +++ b/src/main/java/com/cta4j/alert/common/internal/mapper/Qualifiers.java @@ -1,5 +1,6 @@ package com.cta4j.alert.common.internal.mapper; +import com.cta4j.alert.common.model.ServiceType; import com.cta4j.common.internal.util.BooleanParser; import com.cta4j.common.internal.util.TimestampParser; import org.jetbrains.annotations.ApiStatus; @@ -48,4 +49,21 @@ public static boolean map01ToBoolean(String value) { return BooleanParser.parse01(value); } + + @Named("mapServiceType") + public static ServiceType mapServiceType(String serviceType) { + Objects.requireNonNull(serviceType); + + return switch (serviceType) { + case "B" -> ServiceType.BUS; + case "R" -> ServiceType.RAIL; + case "T" -> ServiceType.STATION; + case "X" -> ServiceType.SYSTEMWIDE; + default -> { + String message = "Unknown service type: %s".formatted(serviceType); + + throw new IllegalArgumentException(message); + } + }; + } } diff --git a/src/main/java/com/cta4j/alert/detailedalert/internal/mapper/AlertMapper.java b/src/main/java/com/cta4j/alert/detailedalert/internal/mapper/AlertMapper.java index 0d494ce0..dd00822c 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/internal/mapper/AlertMapper.java +++ b/src/main/java/com/cta4j/alert/detailedalert/internal/mapper/AlertMapper.java @@ -2,7 +2,9 @@ import com.cta4j.alert.common.internal.mapper.Qualifiers; import com.cta4j.alert.detailedalert.internal.wire.CtaAlert; +import com.cta4j.alert.detailedalert.internal.wire.CtaImpactedService; import com.cta4j.alert.detailedalert.model.Alert; +import com.cta4j.alert.detailedalert.model.ImpactedService; import org.jetbrains.annotations.ApiStatus; import org.mapstruct.Mapper; import org.mapstruct.Mapping; @@ -14,12 +16,25 @@ public interface AlertMapper { AlertMapper INSTANCE = Mappers.getMapper(AlertMapper.class); @Mapping(target = "id", source = "alertId") + @Mapping(target = "fullDescription", source = "fullDescription.cdataSection") @Mapping(target = "severity.score", source = "severityScore") @Mapping(target = "severity.color", source = "severityColor") @Mapping(target = "severity.css", source = "severityCss") @Mapping(target = "startTime", source = "eventStart", qualifiedByName = "mapTimestamp") @Mapping(target = "endTime", source = "eventEnd", qualifiedByName = "mapTimestamp") @Mapping(target = "openEnded", source = "tbd", qualifiedByName = "map01ToBoolean") - //todo: finish mapping + @Mapping(target = "major", source = "majorAlert", qualifiedByName = "map01ToBoolean") + @Mapping(target = "url", source = "alertUrl.cdataSection", qualifiedByName = "mapUri") + @Mapping(target = "impactedServices", source = "impactedService.service") + @Mapping(target = "ttim", source = "ttim") + @Mapping(target = "guid", source = "guid") Alert toDomain(CtaAlert alert); + + @Mapping(target = "type", source = "serviceType", qualifiedByName = "mapServiceType") + @Mapping(target = "typeDescription", source = "serviceTypeDescription") + @Mapping(target = "name", source = "serviceName") + @Mapping(target = "color", source = "serviceBackColor") + @Mapping(target = "textColor", source = "serviceTextColor") + @Mapping(target = "url", source = "serviceUrl.cdataSection", qualifiedByName = "mapUri") + ImpactedService toDomain(CtaImpactedService impactedService); } diff --git a/src/main/java/com/cta4j/alert/detailedalert/model/Alert.java b/src/main/java/com/cta4j/alert/detailedalert/model/Alert.java index 0be7bdf2..fba441bc 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/model/Alert.java +++ b/src/main/java/com/cta4j/alert/detailedalert/model/Alert.java @@ -24,6 +24,10 @@ * @param major whether this alert is of major significance * @param url the URL of this alert's detail page on transitchicago.com * @param impactedServices the services impacted by this alert + * @param ttim an undocumented field returned by the CTA Alerts API; its meaning is not specified and its presence + * is not guaranteed, or {@code null} if not returned + * @param guid an undocumented field returned by the CTA Alerts API that appears to be a stable, globally unique + * identifier for this alert, distinct from {@link #id}, or {@code null} if not returned */ @NullMarked public record Alert( @@ -38,7 +42,9 @@ public record Alert( boolean openEnded, boolean major, URI url, - List impactedServices + List impactedServices, + @Nullable String ttim, + @Nullable String guid ) { /** * Constructs an {@code Alert}. @@ -56,6 +62,10 @@ public record Alert( * @param major whether the alert is of major significance * @param url the URL of the alert's detail page on transitchicago.com * @param impactedServices the services impacted by the alert + * @param ttim an undocumented field returned by the CTA Alerts API; its meaning is not specified and its + * presence is not guaranteed, or {@code null} if not returned + * @param guid an undocumented field returned by the CTA Alerts API that appears to be a stable, globally + * unique identifier for the alert, distinct from {@code id}, or {@code null} if not returned * @throws NullPointerException if {@code id}, {@code headline}, {@code shortDescription}, * {@code fullDescription}, {@code severity}, {@code impact}, {@code startTime}, {@code url}, or * {@code impactedServices} is {@code null}, or if any element of {@code impactedServices} is {@code null} diff --git a/src/main/java/com/cta4j/alert/detailedalert/model/ImpactedService.java b/src/main/java/com/cta4j/alert/detailedalert/model/ImpactedService.java index 7257ada0..b23410af 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/model/ImpactedService.java +++ b/src/main/java/com/cta4j/alert/detailedalert/model/ImpactedService.java @@ -11,6 +11,7 @@ * an alert. * * @param type the type of service this service represents + * @param typeDescription the plain English description of {@code type} (e.g., "Bus Route") * @param name the name of this service (e.g., "Clark", "Red Line", "Jackson", "All Bus Routes") * @param serviceId the identifier of this service; matches GTFS route or station IDs, except for systemwide groupings, * which use a fixed identifier instead (e.g., "22", "Red", "Systemwide") @@ -22,6 +23,7 @@ @NullMarked public record ImpactedService( ServiceType type, + String typeDescription, String name, String serviceId, String color, @@ -32,6 +34,7 @@ public record ImpactedService( * Constructs an {@code ImpactedService}. * * @param type the type of service the service represents + * @param typeDescription the plain English description of {@code type} (e.g., "Bus Route") * @param name the name of the service (e.g., "Clark", "Red Line", "Jackson", "All Bus Routes") * @param serviceId the identifier of the service; matches GTFS route or station IDs, except for systemwide * groupings, which use a fixed identifier instead (e.g., "22", "Red", "Systemwide") @@ -39,11 +42,12 @@ public record ImpactedService( * @param textColor the suggested color of text displayed against {@code color}; casing varies (e.g., "ffffff", * "FFFFFF") * @param url the URL of the service's page on transitchicago.com - * @throws NullPointerException if {@code type}, {@code name}, {@code serviceId}, {@code color}, - * {@code textColor}, or {@code url} is {@code null} + * @throws NullPointerException if {@code type}, {@code typeDescription}, {@code name}, {@code serviceId}, + * {@code color}, {@code textColor}, or {@code url} is {@code null} */ public ImpactedService { Objects.requireNonNull(type); + Objects.requireNonNull(typeDescription); Objects.requireNonNull(name); Objects.requireNonNull(serviceId); Objects.requireNonNull(color); From d1101124a89997efb4eb168621ce6995e61d5bc7 Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Mon, 27 Jul 2026 21:59:04 -0500 Subject: [PATCH 28/60] Refactor mapping methods in AlertMapper and LocationTrainMapper for consistency; add new mapping qualifiers for score, coordinates, and heading --- .../alert/common/internal/mapper/Qualifiers.java | 13 +++++++++++++ .../internal/mapper/AlertMapper.java | 2 +- .../train/common/internal/mapper/Qualifiers.java | 14 +++++++------- .../internal/mapper/LocationTrainMapper.java | 6 +++--- .../cta4j/train/common/TrainQualifiersTest.java | 16 ++++++++-------- 5 files changed, 32 insertions(+), 19 deletions(-) diff --git a/src/main/java/com/cta4j/alert/common/internal/mapper/Qualifiers.java b/src/main/java/com/cta4j/alert/common/internal/mapper/Qualifiers.java index e8783690..21dfd584 100644 --- a/src/main/java/com/cta4j/alert/common/internal/mapper/Qualifiers.java +++ b/src/main/java/com/cta4j/alert/common/internal/mapper/Qualifiers.java @@ -50,6 +50,19 @@ public static boolean map01ToBoolean(String value) { return BooleanParser.parse01(value); } + @Named("mapScore") + public static int mapScore(String value) { + Objects.requireNonNull(value); + + try { + return Integer.parseInt(value); + } catch (NumberFormatException e) { + String message = "Failed to parse score: %s".formatted(value); + + throw new IllegalArgumentException(message, e); + } + } + @Named("mapServiceType") public static ServiceType mapServiceType(String serviceType) { Objects.requireNonNull(serviceType); diff --git a/src/main/java/com/cta4j/alert/detailedalert/internal/mapper/AlertMapper.java b/src/main/java/com/cta4j/alert/detailedalert/internal/mapper/AlertMapper.java index dd00822c..8e12ba6d 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/internal/mapper/AlertMapper.java +++ b/src/main/java/com/cta4j/alert/detailedalert/internal/mapper/AlertMapper.java @@ -17,7 +17,7 @@ public interface AlertMapper { @Mapping(target = "id", source = "alertId") @Mapping(target = "fullDescription", source = "fullDescription.cdataSection") - @Mapping(target = "severity.score", source = "severityScore") + @Mapping(target = "severity.score", source = "severityScore", qualifiedByName = "mapScore") @Mapping(target = "severity.color", source = "severityColor") @Mapping(target = "severity.css", source = "severityCss") @Mapping(target = "startTime", source = "eventStart", qualifiedByName = "mapTimestamp") diff --git a/src/main/java/com/cta4j/train/common/internal/mapper/Qualifiers.java b/src/main/java/com/cta4j/train/common/internal/mapper/Qualifiers.java index a5c59f15..faf5e81e 100644 --- a/src/main/java/com/cta4j/train/common/internal/mapper/Qualifiers.java +++ b/src/main/java/com/cta4j/train/common/internal/mapper/Qualifiers.java @@ -147,8 +147,8 @@ public static TrainDirection map15ToTrainDirection(String direction) { } } - @Named("parseCoordinate") - public static BigDecimal parseCoordinate(String value) { + @Named("mapCoordinate") + public static BigDecimal mapCoordinate(String value) { Objects.requireNonNull(value); try { @@ -160,8 +160,8 @@ public static BigDecimal parseCoordinate(String value) { } } - @Named("parseHeading") - public static int parseHeading(String value) { + @Named("mapHeading") + public static int mapHeading(String value) { Objects.requireNonNull(value); try { @@ -206,9 +206,9 @@ public static int parseHeading(String value) { return null; } - BigDecimal latitude = parseCoordinate(lat); - BigDecimal longitude = parseCoordinate(lon); - int headingValue = parseHeading(heading); + BigDecimal latitude = mapCoordinate(lat); + BigDecimal longitude = mapCoordinate(lon); + int headingValue = mapHeading(heading); return new Coordinates(latitude, longitude, headingValue); } diff --git a/src/main/java/com/cta4j/train/location/internal/mapper/LocationTrainMapper.java b/src/main/java/com/cta4j/train/location/internal/mapper/LocationTrainMapper.java index ed58c768..a02ab452 100644 --- a/src/main/java/com/cta4j/train/location/internal/mapper/LocationTrainMapper.java +++ b/src/main/java/com/cta4j/train/location/internal/mapper/LocationTrainMapper.java @@ -25,8 +25,8 @@ public interface LocationTrainMapper { @Mapping(target = "approaching", source = "isApp", qualifiedByName = "map01ToBoolean") @Mapping(target = "delayed", source = "isDly", qualifiedByName = "map01ToBoolean") @Mapping(target = "flags", source = "flags") - @Mapping(target = "coordinates.latitude", source = "lat", qualifiedByName = "parseCoordinate") - @Mapping(target = "coordinates.longitude", source = "lon", qualifiedByName = "parseCoordinate") - @Mapping(target = "coordinates.heading", source = "heading", qualifiedByName = "parseHeading") + @Mapping(target = "coordinates.latitude", source = "lat", qualifiedByName = "mapCoordinate") + @Mapping(target = "coordinates.longitude", source = "lon", qualifiedByName = "mapCoordinate") + @Mapping(target = "coordinates.heading", source = "heading", qualifiedByName = "mapHeading") LocationTrain toDomain(CtaLocationTrain train); } diff --git a/src/test/java/com/cta4j/train/common/TrainQualifiersTest.java b/src/test/java/com/cta4j/train/common/TrainQualifiersTest.java index e9d5512b..b9b53db9 100644 --- a/src/test/java/com/cta4j/train/common/TrainQualifiersTest.java +++ b/src/test/java/com/cta4j/train/common/TrainQualifiersTest.java @@ -176,25 +176,25 @@ void map15ToTrainDirection_throwsIllegalArgumentException_whenDirectionCodeIsUnr } @Test - void parseCoordinate_returnsBigDecimal_whenValueIsValid() { - assertThat(Qualifiers.parseCoordinate("42.019063")).isEqualByComparingTo(new BigDecimal("42.019063")); + void mapCoordinate_returnsBigDecimal_whenValueIsValid() { + assertThat(Qualifiers.mapCoordinate("42.019063")).isEqualByComparingTo(new BigDecimal("42.019063")); } @Test - void parseCoordinate_throwsIllegalArgumentException_whenValueIsNotNumeric() { + void mapCoordinate_throwsIllegalArgumentException_whenValueIsNotNumeric() { assertThatIllegalArgumentException().isThrownBy(() -> - Qualifiers.parseCoordinate("not-a-number")); + Qualifiers.mapCoordinate("not-a-number")); } @Test - void parseHeading_returnsInt_whenValueIsValid() { - assertThat(Qualifiers.parseHeading("180")).isEqualTo(180); + void mapHeading_returnsInt_whenValueIsValid() { + assertThat(Qualifiers.mapHeading("180")).isEqualTo(180); } @Test - void parseHeading_throwsIllegalArgumentException_whenValueIsNotNumeric() { + void mapHeading_throwsIllegalArgumentException_whenValueIsNotNumeric() { assertThatIllegalArgumentException().isThrownBy(() -> - Qualifiers.parseHeading("not-a-number")); + Qualifiers.mapHeading("not-a-number")); } @Test From 0175e7019d5b5137f8a45567b8f37755127e459d Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Tue, 28 Jul 2026 00:20:51 -0500 Subject: [PATCH 29/60] Implement DetailedAlertsApi and enhance AlertApiImpl; update parameter handling in ArrivalsApiImpl and refine timestamp formatting in Qualifiers --- .../common/internal/impl/AlertApiImpl.java | 5 +- .../common/internal/mapper/Qualifiers.java | 13 +- .../internal/impl/DetailedAlertsApiImpl.java | 244 ++++++++++++++++++ .../internal/impl/ArrivalsApiImpl.java | 4 +- 4 files changed, 263 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/cta4j/alert/detailedalert/internal/impl/DetailedAlertsApiImpl.java diff --git a/src/main/java/com/cta4j/alert/common/internal/impl/AlertApiImpl.java b/src/main/java/com/cta4j/alert/common/internal/impl/AlertApiImpl.java index 79a618e5..a17bc57e 100644 --- a/src/main/java/com/cta4j/alert/common/internal/impl/AlertApiImpl.java +++ b/src/main/java/com/cta4j/alert/common/internal/impl/AlertApiImpl.java @@ -4,6 +4,7 @@ import com.cta4j.alert.common.internal.config.AlertApiConfig; import com.cta4j.alert.common.internal.util.AlertApiConstants; import com.cta4j.alert.detailedalert.DetailedAlertsApi; +import com.cta4j.alert.detailedalert.internal.impl.DetailedAlertsApiImpl; import com.cta4j.alert.routestatus.RouteStatusApi; import com.cta4j.alert.routestatus.internal.impl.RouteStatusApiImpl; import org.jetbrains.annotations.ApiStatus; @@ -16,11 +17,13 @@ @NullMarked public final class AlertApiImpl implements AlertApi { private final RouteStatusApi routeStatusApi; + private final DetailedAlertsApi detailedAlertsApi; public AlertApiImpl(AlertApiConfig config) { Objects.requireNonNull(config); this.routeStatusApi = new RouteStatusApiImpl(config); + this.detailedAlertsApi = new DetailedAlertsApiImpl(config); } @Override @@ -30,7 +33,7 @@ public RouteStatusApi routeStatus() { @Override public DetailedAlertsApi detailedAlerts() { - return null; + return this.detailedAlertsApi; } public static final class BuilderImpl implements AlertApi.Builder { diff --git a/src/main/java/com/cta4j/alert/common/internal/mapper/Qualifiers.java b/src/main/java/com/cta4j/alert/common/internal/mapper/Qualifiers.java index 21dfd584..b862069a 100644 --- a/src/main/java/com/cta4j/alert/common/internal/mapper/Qualifiers.java +++ b/src/main/java/com/cta4j/alert/common/internal/mapper/Qualifiers.java @@ -13,12 +13,23 @@ import java.time.Instant; import java.time.ZoneId; import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.temporal.ChronoField; import java.util.Objects; @ApiStatus.Internal @NullMarked public final class Qualifiers { - private static final DateTimeFormatter TIMESTAMP_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"); + private static final DateTimeFormatter TIMESTAMP_FORMATTER = new DateTimeFormatterBuilder() + .appendPattern("yyyy-MM-dd") + .optionalStart() + .appendPattern("'T'HH:mm:ss") + .optionalEnd() + .parseDefaulting(ChronoField.HOUR_OF_DAY, 0) + .parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0) + .parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0) + .toFormatter(); + private static final ZoneId CHICAGO_ZONE_ID = ZoneId.of("America/Chicago"); private Qualifiers() { diff --git a/src/main/java/com/cta4j/alert/detailedalert/internal/impl/DetailedAlertsApiImpl.java b/src/main/java/com/cta4j/alert/detailedalert/internal/impl/DetailedAlertsApiImpl.java new file mode 100644 index 00000000..e829461d --- /dev/null +++ b/src/main/java/com/cta4j/alert/detailedalert/internal/impl/DetailedAlertsApiImpl.java @@ -0,0 +1,244 @@ +package com.cta4j.alert.detailedalert.internal.impl; + +import com.cta4j.alert.common.internal.config.AlertApiConfig; +import com.cta4j.alert.common.internal.util.AlertApiConstants; +import com.cta4j.alert.detailedalert.DetailedAlertsApi; +import com.cta4j.alert.detailedalert.exception.Cta4jDetailedAlertsException; +import com.cta4j.alert.detailedalert.exception.DetailedAlertsErrorCode; +import com.cta4j.alert.detailedalert.internal.mapper.AlertMapper; +import com.cta4j.alert.detailedalert.internal.wire.CtaAlert; +import com.cta4j.alert.detailedalert.internal.wire.CtaAlerts; +import com.cta4j.alert.detailedalert.internal.wire.CtaDetailedAlertsResponse; +import com.cta4j.alert.detailedalert.model.Alert; +import com.cta4j.alert.detailedalert.query.AlertsQuery; +import com.cta4j.alert.detailedalert.query.BusRouteAlertsQuery; +import com.cta4j.alert.detailedalert.query.LineAlertsQuery; +import com.cta4j.alert.detailedalert.query.StationAlertsQuery; +import com.cta4j.common.train.TrainLine; +import org.apache.hc.client5.http.fluent.Request; +import org.apache.hc.core5.net.URIBuilder; +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; +import tools.jackson.core.JacksonException; +import tools.jackson.databind.json.JsonMapper; + +import java.io.IOException; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +@ApiStatus.Internal +@NullMarked +public final class DetailedAlertsApiImpl implements DetailedAlertsApi { + private final AlertApiConfig config; + + public DetailedAlertsApiImpl(AlertApiConfig config) { + this.config = Objects.requireNonNull(config); + } + + @Override + public List list(AlertsQuery query) { + Objects.requireNonNull(query); + + String activeOnlyString = String.valueOf(query.activeOnly()); + String accessibilityString = String.valueOf(query.accessibility()); + String plannedString = String.valueOf(query.planned()); + + URIBuilder builder = new URIBuilder() + .setScheme(this.config.scheme()) + .setHost(this.config.host()) + .setPort(this.config.port()) + .setPath(AlertApiConstants.DETAILED_ALERTS_ENDPOINT) + .addParameter("activeonly", activeOnlyString) + .addParameter("accessibility", accessibilityString) + .addParameter("planned", plannedString) + .addParameter("outputType", "JSON"); + + return this.makeRequest(builder, query.byStartDate(), query.recentDays()); + } + + @Override + public List findByBusRouteIds(BusRouteAlertsQuery query) { + Objects.requireNonNull(query); + + List routeIds = query.routeIds(); + + if (routeIds.isEmpty()) { + return List.of(); + } + + String activeOnlyString = String.valueOf(query.activeOnly()); + String accessibilityString = String.valueOf(query.accessibility()); + String plannedString = String.valueOf(query.planned()); + String routeIdsString = String.join(",", routeIds); + + return this.makeRequest( + activeOnlyString, + accessibilityString, + plannedString, + routeIdsString, + query.byStartDate(), + query.recentDays() + ); + } + + @Override + public List findByLines(LineAlertsQuery query) { + Objects.requireNonNull(query); + + List lines = query.lines(); + + if (lines.isEmpty()) { + return List.of(); + } + + String activeOnlyString = String.valueOf(query.activeOnly()); + String accessibilityString = String.valueOf(query.accessibility()); + String plannedString = String.valueOf(query.planned()); + + String linesString = lines.stream() + .map(TrainLine::getCode) + .collect(Collectors.joining(",")); + + return this.makeRequest( + activeOnlyString, + accessibilityString, + plannedString, + linesString, + query.byStartDate(), + query.recentDays() + ); + } + + @Override + public List findByStationIds(StationAlertsQuery query) { + Objects.requireNonNull(query); + + List stationIds = query.stationIds(); + + if (stationIds.isEmpty()) { + return List.of(); + } + + String activeOnlyString = String.valueOf(query.activeOnly()); + String accessibilityString = String.valueOf(query.accessibility()); + String plannedString = String.valueOf(query.planned()); + String stationIdsString = String.join(",", stationIds); + + URIBuilder builder = new URIBuilder() + .setScheme(this.config.scheme()) + .setHost(this.config.host()) + .setPort(this.config.port()) + .setPath(AlertApiConstants.DETAILED_ALERTS_ENDPOINT) + .addParameter("activeonly", activeOnlyString) + .addParameter("accessibility", accessibilityString) + .addParameter("planned", plannedString) + .addParameter("stationid", stationIdsString) + .addParameter("outputType", "JSON"); + + return this.makeRequest(builder, query.byStartDate(), query.recentDays()); + } + + private List makeRequest( + String activeOnly, + String accessibility, + String planned, + String routeIds, + @Nullable LocalDate byStartDate, + @Nullable Integer recentDays + ) { + URIBuilder builder = new URIBuilder() + .setScheme(this.config.scheme()) + .setHost(this.config.host()) + .setPort(this.config.port()) + .setPath(AlertApiConstants.DETAILED_ALERTS_ENDPOINT) + .addParameter("activeonly", activeOnly) + .addParameter("accessibility", accessibility) + .addParameter("planned", planned) + .addParameter("routeid", routeIds) + .addParameter("outputType", "JSON"); + + return this.makeRequest(builder, byStartDate, recentDays); + } + + private List makeRequest( + URIBuilder builder, + @Nullable LocalDate byStartDate, + @Nullable Integer recentDays + ) { + if (byStartDate != null) { + String byStartDateString = DateTimeFormatter.BASIC_ISO_DATE.format(byStartDate); + + builder.addParameter("bystartdate", byStartDateString); + } + + if (recentDays != null) { + String recentDaysString = String.valueOf(recentDays); + + builder.addParameter("recentdays", recentDaysString); + } + + String url = builder.toString(); + + String response; + + try { + response = Request.get(url) + .execute() + .returnContent() + .asString(); + } catch (IOException e) { + String message = Objects.requireNonNullElse(e.getMessage(), "Request failed"); + + throw new Cta4jDetailedAlertsException(message, e); + } + + CtaDetailedAlertsResponse detailedAlertsResponse; + + try { + detailedAlertsResponse = JsonMapper.shared() + .readValue(response, CtaDetailedAlertsResponse.class); + } catch (JacksonException e) { + throw new Cta4jDetailedAlertsException("Failed to parse response", e); + } + + CtaAlerts ctaAlerts = detailedAlertsResponse.ctaAlerts(); + + List alert = ctaAlerts.alert(); + + if (alert != null && !alert.isEmpty()) { + return alert.stream() + .map(AlertMapper.INSTANCE::toDomain) + .toList(); + } + + String errorCodeString = ctaAlerts.errorCode(); + + int integerCode; + + try { + integerCode = Integer.parseInt(errorCodeString); + } catch (NumberFormatException e) { + throw new Cta4jDetailedAlertsException("Failed to parse error code", e); + } + + DetailedAlertsErrorCode errorCode = DetailedAlertsErrorCode.fromCode(integerCode); + + if (errorCode == DetailedAlertsErrorCode.OK + || errorCode == DetailedAlertsErrorCode.NO_ACTIVE_ALERTS + || errorCode == DetailedAlertsErrorCode.NO_ACTIVE_ALERTS_FOR_FILTER) { + return List.of(); + } + + String errorMessage = ctaAlerts.errorMessage(); + + String message = errorMessage == null || errorMessage.isBlank() + ? "An unknown error occurred." + : errorMessage; + + throw new Cta4jDetailedAlertsException(message, integerCode); + } +} diff --git a/src/main/java/com/cta4j/train/arrival/internal/impl/ArrivalsApiImpl.java b/src/main/java/com/cta4j/train/arrival/internal/impl/ArrivalsApiImpl.java index 4adcb454..ed3b9f1f 100644 --- a/src/main/java/com/cta4j/train/arrival/internal/impl/ArrivalsApiImpl.java +++ b/src/main/java/com/cta4j/train/arrival/internal/impl/ArrivalsApiImpl.java @@ -79,7 +79,9 @@ private List makeRequest( } if (maxResults != null) { - builder.addParameter("max", maxResults.toString()); + String maxResultString = String.valueOf(maxResults); + + builder.addParameter("max", maxResultString); } String url = builder.toString(); From 53c3837c974eb42bf3b7b9c13cfbf66b255b9f71 Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Tue, 28 Jul 2026 00:36:26 -0500 Subject: [PATCH 30/60] Refactor DetailedAlertsApiImpl to streamline request parameters; replace string conversions with direct boolean and list usage --- .../internal/impl/DetailedAlertsApiImpl.java | 82 ++++++++----------- 1 file changed, 36 insertions(+), 46 deletions(-) diff --git a/src/main/java/com/cta4j/alert/detailedalert/internal/impl/DetailedAlertsApiImpl.java b/src/main/java/com/cta4j/alert/detailedalert/internal/impl/DetailedAlertsApiImpl.java index e829461d..04d67735 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/internal/impl/DetailedAlertsApiImpl.java +++ b/src/main/java/com/cta4j/alert/detailedalert/internal/impl/DetailedAlertsApiImpl.java @@ -28,7 +28,6 @@ import java.time.format.DateTimeFormatter; import java.util.List; import java.util.Objects; -import java.util.stream.Collectors; @ApiStatus.Internal @NullMarked @@ -70,16 +69,12 @@ public List findByBusRouteIds(BusRouteAlertsQuery query) { return List.of(); } - String activeOnlyString = String.valueOf(query.activeOnly()); - String accessibilityString = String.valueOf(query.accessibility()); - String plannedString = String.valueOf(query.planned()); - String routeIdsString = String.join(",", routeIds); - return this.makeRequest( - activeOnlyString, - accessibilityString, - plannedString, - routeIdsString, + query.activeOnly(), + query.accessibility(), + query.planned(), + routeIds, + "routeid", query.byStartDate(), query.recentDays() ); @@ -95,19 +90,16 @@ public List findByLines(LineAlertsQuery query) { return List.of(); } - String activeOnlyString = String.valueOf(query.activeOnly()); - String accessibilityString = String.valueOf(query.accessibility()); - String plannedString = String.valueOf(query.planned()); - - String linesString = lines.stream() - .map(TrainLine::getCode) - .collect(Collectors.joining(",")); + List lineStrings = lines.stream() + .map(TrainLine::getCode) + .toList(); return this.makeRequest( - activeOnlyString, - accessibilityString, - plannedString, - linesString, + query.activeOnly(), + query.accessibility(), + query.planned(), + lineStrings, + "routeid", query.byStartDate(), query.recentDays() ); @@ -123,42 +115,40 @@ public List findByStationIds(StationAlertsQuery query) { return List.of(); } - String activeOnlyString = String.valueOf(query.activeOnly()); - String accessibilityString = String.valueOf(query.accessibility()); - String plannedString = String.valueOf(query.planned()); - String stationIdsString = String.join(",", stationIds); - - URIBuilder builder = new URIBuilder() - .setScheme(this.config.scheme()) - .setHost(this.config.host()) - .setPort(this.config.port()) - .setPath(AlertApiConstants.DETAILED_ALERTS_ENDPOINT) - .addParameter("activeonly", activeOnlyString) - .addParameter("accessibility", accessibilityString) - .addParameter("planned", plannedString) - .addParameter("stationid", stationIdsString) - .addParameter("outputType", "JSON"); - - return this.makeRequest(builder, query.byStartDate(), query.recentDays()); + return this.makeRequest( + query.activeOnly(), + query.accessibility(), + query.planned(), + stationIds, + "stationid", + query.byStartDate(), + query.recentDays() + ); } private List makeRequest( - String activeOnly, - String accessibility, - String planned, - String routeIds, + boolean activeOnly, + boolean accessibility, + boolean planned, + List ids, + String idsParameterName, @Nullable LocalDate byStartDate, @Nullable Integer recentDays ) { + String activeOnlyString = String.valueOf(activeOnly); + String accessibilityString = String.valueOf(accessibility); + String plannedString = String.valueOf(planned); + String idsString = String.join(",", ids); + URIBuilder builder = new URIBuilder() .setScheme(this.config.scheme()) .setHost(this.config.host()) .setPort(this.config.port()) .setPath(AlertApiConstants.DETAILED_ALERTS_ENDPOINT) - .addParameter("activeonly", activeOnly) - .addParameter("accessibility", accessibility) - .addParameter("planned", planned) - .addParameter("routeid", routeIds) + .addParameter("activeonly", activeOnlyString) + .addParameter("accessibility", accessibilityString) + .addParameter("planned", plannedString) + .addParameter(idsParameterName, idsString) .addParameter("outputType", "JSON"); return this.makeRequest(builder, byStartDate, recentDays); From c196f1e0ef84d9284bf36e065f6e9d455eb1101a Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Tue, 28 Jul 2026 11:44:37 -0500 Subject: [PATCH 31/60] Move TrainLine back to the train package and introduce AlertTrainLine given alert queries for Purple Line Express are viable --- .../alert/common/model/AlertTrainLine.java | 108 ++++++++++++++++++ .../detailedalert/DetailedAlertsApi.java | 12 +- .../internal/impl/DetailedAlertsApiImpl.java | 6 +- .../detailedalert/query/LineAlertsQuery.java | 20 ++-- .../query/StationAlertsQuery.java | 4 +- .../alert/routestatus/RouteStatusApi.java | 10 +- .../internal/impl/RouteStatusApiImpl.java | 14 +-- .../internal/impl/ArrivalsApiImpl.java | 2 +- .../train/arrival/query/MapArrivalsQuery.java | 2 +- .../arrival/query/StopArrivalsQuery.java | 2 +- .../common/internal/mapper/Qualifiers.java | 2 +- .../com/cta4j/train/common/model/Arrival.java | 1 - .../common/model}/TrainLine.java | 18 +-- .../cta4j/train/location/LocationsApi.java | 2 +- .../internal/impl/LocationsApiImpl.java | 2 +- .../train/location/model/TrainLocations.java | 2 +- .../station/model/CardinalDirection.java | 8 +- .../cta4j/train/station/model/Station.java | 2 +- .../routestatus/RouteStatusApiImplTest.java | 8 +- .../train/arrival/ArrivalMapperTest.java | 2 +- .../train/arrival/ArrivalsApiImplTest.java | 2 +- .../arrival/query/MapArrivalsQueryTest.java | 2 +- .../arrival/query/StopArrivalsQueryTest.java | 2 +- .../train/common/TrainQualifiersTest.java | 2 +- .../train/common/model/TrainLineTest.java | 1 - .../train/location/LocationsApiImplTest.java | 2 +- .../location/TrainLocationsMapperTest.java | 2 +- .../train/station/StationMapperTest.java | 2 +- .../train/station/StationsApiImplTest.java | 2 +- 29 files changed, 175 insertions(+), 69 deletions(-) create mode 100644 src/main/java/com/cta4j/alert/common/model/AlertTrainLine.java rename src/main/java/com/cta4j/{common/train => train/common/model}/TrainLine.java (89%) diff --git a/src/main/java/com/cta4j/alert/common/model/AlertTrainLine.java b/src/main/java/com/cta4j/alert/common/model/AlertTrainLine.java new file mode 100644 index 00000000..656f6520 --- /dev/null +++ b/src/main/java/com/cta4j/alert/common/model/AlertTrainLine.java @@ -0,0 +1,108 @@ +package com.cta4j.alert.common.model; + +import org.jspecify.annotations.NullMarked; + +import java.util.Objects; + +/** + * Represents a train line as filterable through the CTA Alerts API. + *

+ * Unlike the Train Tracker API, which has no concept of express service, the Alerts API treats the Purple Line + * Express as a distinct route designator ({@code "Pexp"}) from the regular Purple Line ({@code "P"}); per CTA's + * documentation, alerts affecting the Purple Line may be tagged with either designator, or both. + */ +@NullMarked +public enum AlertTrainLine { + /** + * Indicates the Red Line. + */ + RED("Red"), + + /** + * Indicates the Blue Line. + */ + BLUE("Blue"), + + /** + * Indicates the Brown Line. + */ + BROWN("Brn"), + + /** + * Indicates the Green Line. + */ + GREEN("G"), + + /** + * Indicates the Orange Line. + */ + ORANGE("Org"), + + /** + * Indicates the Purple Line, excluding express service. + */ + PURPLE("P"), + + /** + * Indicates the Purple Line Express. + */ + PURPLE_EXPRESS("Pexp"), + + /** + * Indicates the Pink Line. + */ + PINK("Pink"), + + /** + * Indicates the Yellow Line. + */ + YELLOW("Y"); + + /** + * The CTA Alerts API route designator for this train line. + */ + private final String code; + + /** + * Constructs an {@code AlertTrainLine}. + * + * @param code the CTA Alerts API route designator of the train line + * @throws NullPointerException if {@code code} is {@code null} + */ + AlertTrainLine(String code) { + this.code = Objects.requireNonNull(code); + } + + /** + * Gets the CTA Alerts API route designator for this train line. + * + * @return the route designator + */ + public String getCode() { + return this.code; + } + + /** + * Returns the {@code AlertTrainLine} corresponding to the given route designator. + * + * @param code the CTA Alerts API route designator of the train line (case-insensitive) + * @return the corresponding {@code AlertTrainLine} + * @throws IllegalArgumentException if the code does not correspond to any known train line + */ + public static AlertTrainLine fromCode(String code) { + Objects.requireNonNull(code); + + return switch (code.toUpperCase()) { + case "RED" -> AlertTrainLine.RED; + case "BLUE" -> AlertTrainLine.BLUE; + case "BRN" -> AlertTrainLine.BROWN; + case "G" -> AlertTrainLine.GREEN; + case "ORG" -> AlertTrainLine.ORANGE; + case "P" -> AlertTrainLine.PURPLE; + case "PEXP" -> AlertTrainLine.PURPLE_EXPRESS; + case "PINK" -> AlertTrainLine.PINK; + case "Y" -> AlertTrainLine.YELLOW; + default -> throw new IllegalArgumentException("Invalid alert train line: %s".formatted(code)); + }; + } +} diff --git a/src/main/java/com/cta4j/alert/detailedalert/DetailedAlertsApi.java b/src/main/java/com/cta4j/alert/detailedalert/DetailedAlertsApi.java index 0dee0d6a..bce5c7ab 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/DetailedAlertsApi.java +++ b/src/main/java/com/cta4j/alert/detailedalert/DetailedAlertsApi.java @@ -1,12 +1,12 @@ package com.cta4j.alert.detailedalert; +import com.cta4j.alert.common.model.AlertTrainLine; import com.cta4j.alert.detailedalert.exception.Cta4jDetailedAlertsException; import com.cta4j.alert.detailedalert.model.Alert; import com.cta4j.alert.detailedalert.query.AlertsQuery; import com.cta4j.alert.detailedalert.query.BusRouteAlertsQuery; import com.cta4j.alert.detailedalert.query.LineAlertsQuery; import com.cta4j.alert.detailedalert.query.StationAlertsQuery; -import com.cta4j.common.train.TrainLine; import org.jspecify.annotations.NullMarked; import java.util.Collection; @@ -114,10 +114,10 @@ default List findByBusRouteId(String routeId) { * {@code null} * @throws Cta4jDetailedAlertsException if the API returns an error response or the response cannot be parsed */ - default List findByLines(Collection lines) { + default List findByLines(Collection lines) { Objects.requireNonNull(lines); - List linesList = List.copyOf(lines); + List linesList = List.copyOf(lines); LineAlertsQuery query = LineAlertsQuery.builder(linesList) .build(); @@ -134,10 +134,10 @@ default List findByLines(Collection lines) { * @throws NullPointerException if {@code line} is {@code null} * @throws Cta4jDetailedAlertsException if the API returns an error response or the response cannot be parsed */ - default List findByLine(TrainLine line) { + default List findByLine(AlertTrainLine line) { Objects.requireNonNull(line); - List lines = List.of(line); + List lines = List.of(line); return this.findByLines(lines); } @@ -190,4 +190,4 @@ default List findByStationId(String stationId) { return this.findByStationIds(stationIds); } -} \ No newline at end of file +} diff --git a/src/main/java/com/cta4j/alert/detailedalert/internal/impl/DetailedAlertsApiImpl.java b/src/main/java/com/cta4j/alert/detailedalert/internal/impl/DetailedAlertsApiImpl.java index 04d67735..80273826 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/internal/impl/DetailedAlertsApiImpl.java +++ b/src/main/java/com/cta4j/alert/detailedalert/internal/impl/DetailedAlertsApiImpl.java @@ -2,6 +2,7 @@ import com.cta4j.alert.common.internal.config.AlertApiConfig; import com.cta4j.alert.common.internal.util.AlertApiConstants; +import com.cta4j.alert.common.model.AlertTrainLine; import com.cta4j.alert.detailedalert.DetailedAlertsApi; import com.cta4j.alert.detailedalert.exception.Cta4jDetailedAlertsException; import com.cta4j.alert.detailedalert.exception.DetailedAlertsErrorCode; @@ -14,7 +15,6 @@ import com.cta4j.alert.detailedalert.query.BusRouteAlertsQuery; import com.cta4j.alert.detailedalert.query.LineAlertsQuery; import com.cta4j.alert.detailedalert.query.StationAlertsQuery; -import com.cta4j.common.train.TrainLine; import org.apache.hc.client5.http.fluent.Request; import org.apache.hc.core5.net.URIBuilder; import org.jetbrains.annotations.ApiStatus; @@ -84,14 +84,14 @@ public List findByBusRouteIds(BusRouteAlertsQuery query) { public List findByLines(LineAlertsQuery query) { Objects.requireNonNull(query); - List lines = query.lines(); + List lines = query.lines(); if (lines.isEmpty()) { return List.of(); } List lineStrings = lines.stream() - .map(TrainLine::getCode) + .map(AlertTrainLine::getCode) .toList(); return this.makeRequest( diff --git a/src/main/java/com/cta4j/alert/detailedalert/query/LineAlertsQuery.java b/src/main/java/com/cta4j/alert/detailedalert/query/LineAlertsQuery.java index db125101..6911a138 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/query/LineAlertsQuery.java +++ b/src/main/java/com/cta4j/alert/detailedalert/query/LineAlertsQuery.java @@ -1,6 +1,6 @@ package com.cta4j.alert.detailedalert.query; -import com.cta4j.common.train.TrainLine; +import com.cta4j.alert.common.model.AlertTrainLine; import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; @@ -12,7 +12,7 @@ /** * Represents a query for detailed train line alerts. * - * @param lines the {@link List} of {@link TrainLine}s to retrieve alerts for + * @param lines the {@link List} of {@link AlertTrainLine}s to retrieve alerts for * @param activeOnly whether to include only alerts that are currently active * @param accessibility whether to include alerts that affect accessible paths in stations * @param planned whether to include common planned alerts @@ -22,7 +22,7 @@ */ @NullMarked public record LineAlertsQuery( - List lines, + List lines, boolean activeOnly, boolean accessibility, boolean planned, @@ -32,7 +32,7 @@ public record LineAlertsQuery( /** * Constructs a {@code LineAlertsQuery}. * - * @param lines the {@link List} of {@link TrainLine}s to retrieve alerts for + * @param lines the {@link List} of {@link AlertTrainLine}s to retrieve alerts for * @param activeOnly whether to include only alerts that are currently active * @param accessibility whether to include alerts that affect accessible paths in stations * @param planned whether to include common planned alerts @@ -61,12 +61,12 @@ public record LineAlertsQuery( /** * Creates a builder for {@code LineAlertsQuery}. * - * @param lines the {@link Collection} of {@link TrainLine}s to retrieve alerts for + * @param lines the {@link Collection} of {@link AlertTrainLine}s to retrieve alerts for * @return a new {@code Builder} instance * @throws NullPointerException if {@code lines} is {@code null}, or if any element of {@code lines} is * {@code null} */ - public static Builder builder(Collection lines) { + public static Builder builder(Collection lines) { return new Builder(lines); } @@ -75,9 +75,9 @@ public static Builder builder(Collection lines) { */ public static final class Builder { /** - * The {@link List} of {@link TrainLine}s to retrieve alerts for. + * The {@link List} of {@link AlertTrainLine}s to retrieve alerts for. */ - private final List lines; + private final List lines; /** * Whether to include only alerts that are currently active. @@ -112,11 +112,11 @@ public static final class Builder { * By default, {@code activeOnly} is {@code false}, and {@code accessibility} and {@code planned} are * {@code true}, matching the CTA Alerts API's own defaults. * - * @param lines the {@link Collection} of {@link TrainLine}s to retrieve alerts for + * @param lines the {@link Collection} of {@link AlertTrainLine}s to retrieve alerts for * @throws NullPointerException if {@code lines} is {@code null}, or if any element of {@code lines} is * {@code null} */ - public Builder(Collection lines) { + public Builder(Collection lines) { Objects.requireNonNull(lines); this.lines = List.copyOf(lines); diff --git a/src/main/java/com/cta4j/alert/detailedalert/query/StationAlertsQuery.java b/src/main/java/com/cta4j/alert/detailedalert/query/StationAlertsQuery.java index fe23feaa..5c4bdda9 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/query/StationAlertsQuery.java +++ b/src/main/java/com/cta4j/alert/detailedalert/query/StationAlertsQuery.java @@ -112,8 +112,8 @@ public static final class Builder { * {@code true}, matching the CTA Alerts API's own defaults. * * @param stationIds the {@link Collection} of train station IDs to retrieve alerts for - * @throws NullPointerException if {@code stationIds} is {@code null}, or if any element of {@code stationIds} is - * {@code null} + * @throws NullPointerException if {@code stationIds} is {@code null}, or if any element of {@code stationIds} + * is {@code null} */ public Builder(Collection stationIds) { Objects.requireNonNull(stationIds); diff --git a/src/main/java/com/cta4j/alert/routestatus/RouteStatusApi.java b/src/main/java/com/cta4j/alert/routestatus/RouteStatusApi.java index 30787666..26f7746e 100644 --- a/src/main/java/com/cta4j/alert/routestatus/RouteStatusApi.java +++ b/src/main/java/com/cta4j/alert/routestatus/RouteStatusApi.java @@ -1,9 +1,9 @@ package com.cta4j.alert.routestatus; +import com.cta4j.alert.common.model.AlertTrainLine; import com.cta4j.alert.routestatus.exception.Cta4jRouteStatusException; import com.cta4j.alert.routestatus.model.RouteStatus; import com.cta4j.alert.common.model.ServiceType; -import com.cta4j.common.train.TrainLine; import org.jspecify.annotations.NullMarked; import java.util.Collection; @@ -75,7 +75,7 @@ default List findByType(ServiceType type) { * no route statuses are found for the bus route ID * @throws NullPointerException if {@code routeId} is {@code null} * @throws IllegalArgumentException if {@code routeId} matches a train line code (e.g., "Red"); use - * {@link #findByLine(TrainLine)} instead + * {@link #findByLine(AlertTrainLine)} instead * @throws Cta4jRouteStatusException if the API returns an error response or the response cannot be parsed */ default List findByBusRouteId(String routeId) { @@ -95,7 +95,7 @@ default List findByBusRouteId(String routeId) { * @throws NullPointerException if {@code lines} is {@code null} or contains {@code null} elements * @throws Cta4jRouteStatusException if the API returns an error response or the response cannot be parsed */ - List findByLines(Collection lines); + List findByLines(Collection lines); /** * Retrieves route statuses for the specified train line. @@ -106,10 +106,10 @@ default List findByBusRouteId(String routeId) { * @throws NullPointerException if {@code line} is {@code null} * @throws Cta4jRouteStatusException if the API returns an error response or the response cannot be parsed */ - default List findByLine(TrainLine line) { + default List findByLine(AlertTrainLine line) { Objects.requireNonNull(line); - List lines = List.of(line); + List lines = List.of(line); return this.findByLines(lines); } diff --git a/src/main/java/com/cta4j/alert/routestatus/internal/impl/RouteStatusApiImpl.java b/src/main/java/com/cta4j/alert/routestatus/internal/impl/RouteStatusApiImpl.java index 218606c5..c51e6a00 100644 --- a/src/main/java/com/cta4j/alert/routestatus/internal/impl/RouteStatusApiImpl.java +++ b/src/main/java/com/cta4j/alert/routestatus/internal/impl/RouteStatusApiImpl.java @@ -2,6 +2,7 @@ import com.cta4j.alert.common.internal.config.AlertApiConfig; import com.cta4j.alert.common.internal.util.AlertApiConstants; +import com.cta4j.alert.common.model.AlertTrainLine; import com.cta4j.alert.routestatus.RouteStatusApi; import com.cta4j.alert.routestatus.exception.Cta4jRouteStatusException; import com.cta4j.alert.routestatus.exception.RouteStatusErrorCode; @@ -11,7 +12,6 @@ import com.cta4j.alert.routestatus.internal.wire.CtaRoutes; import com.cta4j.alert.routestatus.model.RouteStatus; import com.cta4j.alert.common.model.ServiceType; -import com.cta4j.common.train.TrainLine; import org.apache.hc.client5.http.fluent.Request; import org.apache.hc.core5.net.URIBuilder; import org.jetbrains.annotations.ApiStatus; @@ -94,7 +94,7 @@ public List findByBusRouteIds(Collection routeIds) { if (isTrainLine(routeId)) { String message = """ %s is a train line, not a bus route; \ - use findByLines(Collection) instead""".formatted(routeId); + use findByLines(Collection) instead""".formatted(routeId); throw new IllegalArgumentException(message); } @@ -115,17 +115,17 @@ public List findByBusRouteIds(Collection routeIds) { } @Override - public List findByLines(Collection lines) { + public List findByLines(Collection lines) { Objects.requireNonNull(lines); - List linesList = List.copyOf(lines); + List linesList = List.copyOf(lines); if (linesList.isEmpty()) { return List.of(); } String linesString = linesList.stream() - .map(TrainLine::getCode) + .map(AlertTrainLine::getCode) .collect(Collectors.joining(",")); String url = new URIBuilder() @@ -239,8 +239,8 @@ private List makeRequest(String url) { } private static boolean isTrainLine(String routeId) { - return Arrays.stream(TrainLine.values()) - .map(TrainLine::getCode) + return Arrays.stream(AlertTrainLine.values()) + .map(AlertTrainLine::getCode) .anyMatch(code -> code.equalsIgnoreCase(routeId)); } } diff --git a/src/main/java/com/cta4j/train/arrival/internal/impl/ArrivalsApiImpl.java b/src/main/java/com/cta4j/train/arrival/internal/impl/ArrivalsApiImpl.java index ed3b9f1f..c3f8566c 100644 --- a/src/main/java/com/cta4j/train/arrival/internal/impl/ArrivalsApiImpl.java +++ b/src/main/java/com/cta4j/train/arrival/internal/impl/ArrivalsApiImpl.java @@ -12,7 +12,7 @@ import com.cta4j.train.common.internal.wire.CtaArrival; import com.cta4j.train.common.internal.wire.CtaResponse; import com.cta4j.train.common.model.Arrival; -import com.cta4j.common.train.TrainLine; +import com.cta4j.train.common.model.TrainLine; import org.apache.hc.client5.http.fluent.Request; import org.apache.hc.core5.net.URIBuilder; import org.jetbrains.annotations.ApiStatus; diff --git a/src/main/java/com/cta4j/train/arrival/query/MapArrivalsQuery.java b/src/main/java/com/cta4j/train/arrival/query/MapArrivalsQuery.java index af2fe7af..87ffafb7 100644 --- a/src/main/java/com/cta4j/train/arrival/query/MapArrivalsQuery.java +++ b/src/main/java/com/cta4j/train/arrival/query/MapArrivalsQuery.java @@ -1,6 +1,6 @@ package com.cta4j.train.arrival.query; -import com.cta4j.common.train.TrainLine; +import com.cta4j.train.common.model.TrainLine; import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; diff --git a/src/main/java/com/cta4j/train/arrival/query/StopArrivalsQuery.java b/src/main/java/com/cta4j/train/arrival/query/StopArrivalsQuery.java index 94525bd8..23b84051 100644 --- a/src/main/java/com/cta4j/train/arrival/query/StopArrivalsQuery.java +++ b/src/main/java/com/cta4j/train/arrival/query/StopArrivalsQuery.java @@ -1,6 +1,6 @@ package com.cta4j.train.arrival.query; -import com.cta4j.common.train.TrainLine; +import com.cta4j.train.common.model.TrainLine; import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; diff --git a/src/main/java/com/cta4j/train/common/internal/mapper/Qualifiers.java b/src/main/java/com/cta4j/train/common/internal/mapper/Qualifiers.java index faf5e81e..6368a4e4 100644 --- a/src/main/java/com/cta4j/train/common/internal/mapper/Qualifiers.java +++ b/src/main/java/com/cta4j/train/common/internal/mapper/Qualifiers.java @@ -5,7 +5,7 @@ import com.cta4j.common.internal.util.TimestampParser; import com.cta4j.train.common.internal.wire.CtaArrival; import com.cta4j.train.common.model.TrainDirection; -import com.cta4j.common.train.TrainLine; +import com.cta4j.train.common.model.TrainLine; import com.cta4j.train.follow.internal.wire.CtaPosition; import com.cta4j.train.station.internal.wire.CtaStation; import com.cta4j.train.station.model.CardinalDirection; diff --git a/src/main/java/com/cta4j/train/common/model/Arrival.java b/src/main/java/com/cta4j/train/common/model/Arrival.java index d979553e..3b4eab09 100644 --- a/src/main/java/com/cta4j/train/common/model/Arrival.java +++ b/src/main/java/com/cta4j/train/common/model/Arrival.java @@ -1,6 +1,5 @@ package com.cta4j.train.common.model; -import com.cta4j.common.train.TrainLine; import org.jspecify.annotations.NullMarked; import java.time.Instant; diff --git a/src/main/java/com/cta4j/common/train/TrainLine.java b/src/main/java/com/cta4j/train/common/model/TrainLine.java similarity index 89% rename from src/main/java/com/cta4j/common/train/TrainLine.java rename to src/main/java/com/cta4j/train/common/model/TrainLine.java index 59244e03..483bc8c8 100644 --- a/src/main/java/com/cta4j/common/train/TrainLine.java +++ b/src/main/java/com/cta4j/train/common/model/TrainLine.java @@ -1,4 +1,4 @@ -package com.cta4j.common.train; +package com.cta4j.train.common.model; import org.jspecify.annotations.NullMarked; @@ -10,42 +10,42 @@ @NullMarked public enum TrainLine { /** - * The Red Line. + * Indicates the Red Line. */ RED("Red", "#C60C30"), /** - * The Blue Line. + * Indicates the Blue Line. */ BLUE("Blue", "#00A1DE"), /** - * The Brown Line. + * Indicates the Brown Line. */ BROWN("Brn", "#62361B"), /** - * The Green Line. + * Indicates the Green Line. */ GREEN("G", "#009B3A"), /** - * The Orange Line. + * Indicates the Orange Line. */ ORANGE("Org", "#F9461C"), /** - * The Purple Line. + * Indicates the Purple Line. */ PURPLE("P", "#522398"), /** - * The Pink Line. + * Indicates the Pink Line. */ PINK("Pink", "#E27EA6"), /** - * The Yellow Line. + * Indicates the Yellow Line. */ YELLOW("Y", "#F9E300"); diff --git a/src/main/java/com/cta4j/train/location/LocationsApi.java b/src/main/java/com/cta4j/train/location/LocationsApi.java index c4db0360..541fad13 100644 --- a/src/main/java/com/cta4j/train/location/LocationsApi.java +++ b/src/main/java/com/cta4j/train/location/LocationsApi.java @@ -1,6 +1,6 @@ package com.cta4j.train.location; -import com.cta4j.common.train.TrainLine; +import com.cta4j.train.common.model.TrainLine; import com.cta4j.train.location.exception.Cta4jLocationsException; import com.cta4j.train.location.model.TrainLocations; import org.jspecify.annotations.NullMarked; diff --git a/src/main/java/com/cta4j/train/location/internal/impl/LocationsApiImpl.java b/src/main/java/com/cta4j/train/location/internal/impl/LocationsApiImpl.java index c17d18d2..bb7a59e8 100644 --- a/src/main/java/com/cta4j/train/location/internal/impl/LocationsApiImpl.java +++ b/src/main/java/com/cta4j/train/location/internal/impl/LocationsApiImpl.java @@ -3,7 +3,7 @@ import com.cta4j.train.common.internal.config.TrainApiConfig; import com.cta4j.train.common.internal.util.TrainApiConstants; import com.cta4j.train.common.internal.wire.CtaResponse; -import com.cta4j.common.train.TrainLine; +import com.cta4j.train.common.model.TrainLine; import com.cta4j.train.location.LocationsApi; import com.cta4j.train.location.exception.Cta4jLocationsException; import com.cta4j.train.location.exception.LocationsErrorCode; diff --git a/src/main/java/com/cta4j/train/location/model/TrainLocations.java b/src/main/java/com/cta4j/train/location/model/TrainLocations.java index f1dfc739..5629cf27 100644 --- a/src/main/java/com/cta4j/train/location/model/TrainLocations.java +++ b/src/main/java/com/cta4j/train/location/model/TrainLocations.java @@ -1,6 +1,6 @@ package com.cta4j.train.location.model; -import com.cta4j.common.train.TrainLine; +import com.cta4j.train.common.model.TrainLine; import org.jspecify.annotations.NullMarked; import java.util.List; diff --git a/src/main/java/com/cta4j/train/station/model/CardinalDirection.java b/src/main/java/com/cta4j/train/station/model/CardinalDirection.java index 19302aa4..c2c7c849 100644 --- a/src/main/java/com/cta4j/train/station/model/CardinalDirection.java +++ b/src/main/java/com/cta4j/train/station/model/CardinalDirection.java @@ -10,22 +10,22 @@ @NullMarked public enum CardinalDirection { /** - * North direction. + * Indicates the north direction. */ NORTH, /** - * East direction. + * Indicates the east direction. */ EAST, /** - * South direction. + * Indicates the south direction. */ SOUTH, /** - * West direction. + * Indicates the west direction. */ WEST; diff --git a/src/main/java/com/cta4j/train/station/model/Station.java b/src/main/java/com/cta4j/train/station/model/Station.java index 00cdd3a9..a8396cc4 100644 --- a/src/main/java/com/cta4j/train/station/model/Station.java +++ b/src/main/java/com/cta4j/train/station/model/Station.java @@ -1,6 +1,6 @@ package com.cta4j.train.station.model; -import com.cta4j.common.train.TrainLine; +import com.cta4j.train.common.model.TrainLine; import org.jspecify.annotations.NullMarked; import java.util.Objects; diff --git a/src/test/java/com/cta4j/alert/routestatus/RouteStatusApiImplTest.java b/src/test/java/com/cta4j/alert/routestatus/RouteStatusApiImplTest.java index c8100204..b5c4cd1d 100644 --- a/src/test/java/com/cta4j/alert/routestatus/RouteStatusApiImplTest.java +++ b/src/test/java/com/cta4j/alert/routestatus/RouteStatusApiImplTest.java @@ -8,7 +8,7 @@ import com.cta4j.alert.routestatus.internal.impl.RouteStatusApiImpl; import com.cta4j.alert.routestatus.model.RouteStatus; import com.cta4j.alert.common.model.ServiceType; -import com.cta4j.common.train.TrainLine; +import com.cta4j.alert.common.model.AlertTrainLine; import com.github.tomakehurst.wiremock.WireMockServer; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -366,7 +366,7 @@ void findByLines_sendsRouteidParameter_asCommaJoinedCodes() { .withHeader("Content-Type", "application/json") .withBody(TestFixtures.read("alert/routestatus/rail_success.json")))); - List statuses = this.api.findByLines(List.of(TrainLine.RED, TrainLine.BLUE)); + List statuses = this.api.findByLines(List.of(AlertTrainLine.RED, AlertTrainLine.BLUE)); assertThat(statuses).hasSize(2); } @@ -379,7 +379,7 @@ void findByLines_returnsRouteStatuses_whenResponseContainsData() { .withHeader("Content-Type", "application/json") .withBody(TestFixtures.read("alert/routestatus/rail_success.json")))); - List statuses = this.api.findByLines(List.of(TrainLine.RED, TrainLine.BLUE)); + List statuses = this.api.findByLines(List.of(AlertTrainLine.RED, AlertTrainLine.BLUE)); assertThat(statuses).hasSize(2); RouteStatus redLine = statuses.getFirst(); @@ -397,7 +397,7 @@ void findByLine_delegatesToFindByLines() { .withHeader("Content-Type", "application/json") .withBody(TestFixtures.read("alert/routestatus/rail_success.json")))); - List statuses = this.api.findByLine(TrainLine.RED); + List statuses = this.api.findByLine(AlertTrainLine.RED); assertThat(statuses).hasSize(2); } diff --git a/src/test/java/com/cta4j/train/arrival/ArrivalMapperTest.java b/src/test/java/com/cta4j/train/arrival/ArrivalMapperTest.java index f7ee6f18..71ab7c35 100644 --- a/src/test/java/com/cta4j/train/arrival/ArrivalMapperTest.java +++ b/src/test/java/com/cta4j/train/arrival/ArrivalMapperTest.java @@ -3,7 +3,7 @@ import com.cta4j.train.common.internal.mapper.ArrivalMapper; import com.cta4j.train.common.internal.wire.CtaArrival; import com.cta4j.train.common.model.Arrival; -import com.cta4j.common.train.TrainLine; +import com.cta4j.train.common.model.TrainLine; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.*; diff --git a/src/test/java/com/cta4j/train/arrival/ArrivalsApiImplTest.java b/src/test/java/com/cta4j/train/arrival/ArrivalsApiImplTest.java index 313d845e..49b282c7 100644 --- a/src/test/java/com/cta4j/train/arrival/ArrivalsApiImplTest.java +++ b/src/test/java/com/cta4j/train/arrival/ArrivalsApiImplTest.java @@ -8,7 +8,7 @@ import com.cta4j.train.arrival.query.StopArrivalsQuery; import com.cta4j.train.common.internal.config.TrainApiConfig; import com.cta4j.train.common.model.Arrival; -import com.cta4j.common.train.TrainLine; +import com.cta4j.train.common.model.TrainLine; import com.github.tomakehurst.wiremock.WireMockServer; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; diff --git a/src/test/java/com/cta4j/train/arrival/query/MapArrivalsQueryTest.java b/src/test/java/com/cta4j/train/arrival/query/MapArrivalsQueryTest.java index 7b8e572d..1d04c154 100644 --- a/src/test/java/com/cta4j/train/arrival/query/MapArrivalsQueryTest.java +++ b/src/test/java/com/cta4j/train/arrival/query/MapArrivalsQueryTest.java @@ -1,6 +1,6 @@ package com.cta4j.train.arrival.query; -import com.cta4j.common.train.TrainLine; +import com.cta4j.train.common.model.TrainLine; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.*; diff --git a/src/test/java/com/cta4j/train/arrival/query/StopArrivalsQueryTest.java b/src/test/java/com/cta4j/train/arrival/query/StopArrivalsQueryTest.java index 18037432..1248a359 100644 --- a/src/test/java/com/cta4j/train/arrival/query/StopArrivalsQueryTest.java +++ b/src/test/java/com/cta4j/train/arrival/query/StopArrivalsQueryTest.java @@ -1,6 +1,6 @@ package com.cta4j.train.arrival.query; -import com.cta4j.common.train.TrainLine; +import com.cta4j.train.common.model.TrainLine; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.*; diff --git a/src/test/java/com/cta4j/train/common/TrainQualifiersTest.java b/src/test/java/com/cta4j/train/common/TrainQualifiersTest.java index b9b53db9..4e134545 100644 --- a/src/test/java/com/cta4j/train/common/TrainQualifiersTest.java +++ b/src/test/java/com/cta4j/train/common/TrainQualifiersTest.java @@ -4,7 +4,7 @@ import com.cta4j.train.common.internal.mapper.Qualifiers; import com.cta4j.train.common.internal.wire.CtaArrival; import com.cta4j.train.common.model.TrainDirection; -import com.cta4j.common.train.TrainLine; +import com.cta4j.train.common.model.TrainLine; import com.cta4j.train.follow.internal.wire.CtaPosition; import com.cta4j.train.station.internal.wire.CtaLocation; import com.cta4j.train.station.internal.wire.CtaStation; diff --git a/src/test/java/com/cta4j/train/common/model/TrainLineTest.java b/src/test/java/com/cta4j/train/common/model/TrainLineTest.java index 00ed4e8a..4ffd8f29 100644 --- a/src/test/java/com/cta4j/train/common/model/TrainLineTest.java +++ b/src/test/java/com/cta4j/train/common/model/TrainLineTest.java @@ -1,6 +1,5 @@ package com.cta4j.train.common.model; -import com.cta4j.common.train.TrainLine; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.*; diff --git a/src/test/java/com/cta4j/train/location/LocationsApiImplTest.java b/src/test/java/com/cta4j/train/location/LocationsApiImplTest.java index 69ad14ac..52ec2f01 100644 --- a/src/test/java/com/cta4j/train/location/LocationsApiImplTest.java +++ b/src/test/java/com/cta4j/train/location/LocationsApiImplTest.java @@ -2,7 +2,7 @@ import com.cta4j.TestFixtures; import com.cta4j.train.common.internal.config.TrainApiConfig; -import com.cta4j.common.train.TrainLine; +import com.cta4j.train.common.model.TrainLine; import com.cta4j.train.location.exception.Cta4jLocationsException; import com.cta4j.train.location.exception.LocationsErrorCode; import com.cta4j.train.location.internal.impl.LocationsApiImpl; diff --git a/src/test/java/com/cta4j/train/location/TrainLocationsMapperTest.java b/src/test/java/com/cta4j/train/location/TrainLocationsMapperTest.java index a0c4feef..edb5ba14 100644 --- a/src/test/java/com/cta4j/train/location/TrainLocationsMapperTest.java +++ b/src/test/java/com/cta4j/train/location/TrainLocationsMapperTest.java @@ -1,6 +1,6 @@ package com.cta4j.train.location; -import com.cta4j.common.train.TrainLine; +import com.cta4j.train.common.model.TrainLine; import com.cta4j.train.location.internal.mapper.TrainLocationsMapper; import com.cta4j.train.location.internal.wire.CtaLocationTrain; import com.cta4j.train.location.internal.wire.CtaRoute; diff --git a/src/test/java/com/cta4j/train/station/StationMapperTest.java b/src/test/java/com/cta4j/train/station/StationMapperTest.java index 5850ef97..cf0be6e5 100644 --- a/src/test/java/com/cta4j/train/station/StationMapperTest.java +++ b/src/test/java/com/cta4j/train/station/StationMapperTest.java @@ -1,6 +1,6 @@ package com.cta4j.train.station; -import com.cta4j.common.train.TrainLine; +import com.cta4j.train.common.model.TrainLine; import com.cta4j.train.station.internal.mapper.StationMapper; import com.cta4j.train.station.internal.wire.CtaLocation; import com.cta4j.train.station.internal.wire.CtaStation; diff --git a/src/test/java/com/cta4j/train/station/StationsApiImplTest.java b/src/test/java/com/cta4j/train/station/StationsApiImplTest.java index 9e14d760..a1d7f6c1 100644 --- a/src/test/java/com/cta4j/train/station/StationsApiImplTest.java +++ b/src/test/java/com/cta4j/train/station/StationsApiImplTest.java @@ -3,7 +3,7 @@ import com.cta4j.TestFixtures; import com.cta4j.train.common.exception.Cta4jTrainException; import com.cta4j.train.common.internal.config.TrainApiConfig; -import com.cta4j.common.train.TrainLine; +import com.cta4j.train.common.model.TrainLine; import com.cta4j.train.station.internal.impl.StationsApiImpl; import com.cta4j.train.station.model.Station; import com.github.tomakehurst.wiremock.WireMockServer; From 14d66a488b7bb6931e38ceaafb7dd8efa8c96b91 Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Tue, 28 Jul 2026 12:12:20 -0500 Subject: [PATCH 32/60] Refactor CtaAlert and DetailedAlertsApiImpl to make ttim and guid fields nullable; enhance error handling for bus route queries to prevent train line code usage --- CLAUDE.md | 29 +++++++++++++++++++ .../detailedalert/DetailedAlertsApi.java | 6 ++++ .../internal/impl/DetailedAlertsApiImpl.java | 17 +++++++++++ .../internal/mapper/AlertMapper.java | 2 -- .../detailedalert/internal/wire/CtaAlert.java | 4 +-- 5 files changed, 54 insertions(+), 4 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 5f7bb558..a8d14d1d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -100,3 +100,32 @@ MapStruct mappers live in `internal/mapper/`. They are interfaces annotated with - No `Optional` for fields or parameters. Use method overloading or `@Nullable` fields instead. - Prefer `List.of()` for empty returns; use `List.copyOf()` for defensive copies. - All files must end with a trailing newline. + +## Javadoc Conventions + +This project uses Markdown documentation comments (`///`, JEP 467, JDK 23+) +instead of traditional `/** */` HTML Javadoc. Do not use `{@code}`, `{@link}`, +or HTML tags — use plain Markdown (backticks, `[Type]` links, etc.). + +References: +- Content/style conventions (summary sentence, tag usage): + https://www.oracle.com/technical-resources/articles/java/javadoc-tool.html +- Markdown comment syntax (`///`, CommonMark extensions, JDK 23+): + https://docs.oracle.com/en/java/javase/25/javadoc/using-markdown-documentation-comments.html + +- **Summary sentence:** The first line is a standalone summary fragment + ending in a period, third-person descriptive ("Returns the active + arrivals for a station," not "This method returns..."). +- **Tag order:** `@param` → `@return` → `@deprecated` → `@since` → `@throws` + → `@see`. +- **@param / @throws descriptions:** Lowercase phrase, no trailing period. +- **Code references:** Use backtick spans (`` `RoutesApi` ``, `` `List` ``) + instead of `{@code}`. Use Markdown reference links (`[RoutesApi]`) instead + of `{@link}` only when the cross-reference meaningfully aids understanding. +- **What gets documented:** Public interfaces (`*Api`), public domain models + (`model/`), and builders always. Wire records (`internal/wire/`), mappers, + and `*ApiImpl` classes are `@ApiStatus.Internal` and are not documented + unless the "why" is non-obvious (per Code Style). +- **Package docs:** Each top-level feature package (e.g. `bus.route`, + `train.arrivals`) gets a `package-info.java` with a one-paragraph summary + of the feature's responsibility, written in the same Markdown style. diff --git a/src/main/java/com/cta4j/alert/detailedalert/DetailedAlertsApi.java b/src/main/java/com/cta4j/alert/detailedalert/DetailedAlertsApi.java index bce5c7ab..26dee936 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/DetailedAlertsApi.java +++ b/src/main/java/com/cta4j/alert/detailedalert/DetailedAlertsApi.java @@ -51,6 +51,8 @@ default List list() { * @return a {@link List} of {@link Alert}s corresponding to the provided bus route IDs, or an empty {@link List} * if no alerts are found * @throws NullPointerException if {@code query} is {@code null} + * @throws IllegalArgumentException if any of the query's route IDs matches a train line code (e.g., "Red"); use + * {@link #findByLines(LineAlertsQuery)} instead * @throws Cta4jDetailedAlertsException if the API returns an error response or the response cannot be parsed */ List findByBusRouteIds(BusRouteAlertsQuery query); @@ -63,6 +65,8 @@ default List list() { * if no alerts are found * @throws NullPointerException if {@code routeIds} is {@code null}, or if any element of {@code routeIds} is * {@code null} + * @throws IllegalArgumentException if any of the {@code routeIds} matches a train line code (e.g., "Red"); use + * {@link #findByLines(Collection)} instead * @throws Cta4jDetailedAlertsException if the API returns an error response or the response cannot be parsed */ default List findByBusRouteIds(Collection routeIds) { @@ -83,6 +87,8 @@ default List findByBusRouteIds(Collection routeIds) { * @return a {@link List} of {@link Alert}s corresponding to the provided bus route ID, or an empty {@link List} * if no alerts are found * @throws NullPointerException if {@code routeId} is {@code null} + * @throws IllegalArgumentException if {@code routeId} matches a train line code (e.g., "Red"); use + * {@link #findByLine(AlertTrainLine)} instead * @throws Cta4jDetailedAlertsException if the API returns an error response or the response cannot be parsed */ default List findByBusRouteId(String routeId) { diff --git a/src/main/java/com/cta4j/alert/detailedalert/internal/impl/DetailedAlertsApiImpl.java b/src/main/java/com/cta4j/alert/detailedalert/internal/impl/DetailedAlertsApiImpl.java index 80273826..7cfe27de 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/internal/impl/DetailedAlertsApiImpl.java +++ b/src/main/java/com/cta4j/alert/detailedalert/internal/impl/DetailedAlertsApiImpl.java @@ -26,6 +26,7 @@ import java.io.IOException; import java.time.LocalDate; import java.time.format.DateTimeFormatter; +import java.util.Arrays; import java.util.List; import java.util.Objects; @@ -69,6 +70,16 @@ public List findByBusRouteIds(BusRouteAlertsQuery query) { return List.of(); } + for (String routeId : routeIds) { + if (isTrainLine(routeId)) { + String message = """ + %s is a train line, not a bus route; \ + use findByLines(Collection) instead""".formatted(routeId); + + throw new IllegalArgumentException(message); + } + } + return this.makeRequest( query.activeOnly(), query.accessibility(), @@ -231,4 +242,10 @@ private List makeRequest( throw new Cta4jDetailedAlertsException(message, integerCode); } + + private static boolean isTrainLine(String routeId) { + return Arrays.stream(AlertTrainLine.values()) + .map(AlertTrainLine::getCode) + .anyMatch(code -> code.equalsIgnoreCase(routeId)); + } } diff --git a/src/main/java/com/cta4j/alert/detailedalert/internal/mapper/AlertMapper.java b/src/main/java/com/cta4j/alert/detailedalert/internal/mapper/AlertMapper.java index 8e12ba6d..181c769d 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/internal/mapper/AlertMapper.java +++ b/src/main/java/com/cta4j/alert/detailedalert/internal/mapper/AlertMapper.java @@ -26,8 +26,6 @@ public interface AlertMapper { @Mapping(target = "major", source = "majorAlert", qualifiedByName = "map01ToBoolean") @Mapping(target = "url", source = "alertUrl.cdataSection", qualifiedByName = "mapUri") @Mapping(target = "impactedServices", source = "impactedService.service") - @Mapping(target = "ttim", source = "ttim") - @Mapping(target = "guid", source = "guid") Alert toDomain(CtaAlert alert); @Mapping(target = "type", source = "serviceType", qualifiedByName = "mapServiceType") diff --git a/src/main/java/com/cta4j/alert/detailedalert/internal/wire/CtaAlert.java b/src/main/java/com/cta4j/alert/detailedalert/internal/wire/CtaAlert.java index 7fcabad2..569b69b2 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/internal/wire/CtaAlert.java +++ b/src/main/java/com/cta4j/alert/detailedalert/internal/wire/CtaAlert.java @@ -57,9 +57,11 @@ public record CtaAlert( CtaImpactedServices impactedService, @JsonProperty("ttim") + @Nullable String ttim, @JsonProperty("GUID") + @Nullable String guid ) { public CtaAlert { @@ -76,7 +78,5 @@ public record CtaAlert( Objects.requireNonNull(majorAlert); Objects.requireNonNull(alertUrl); Objects.requireNonNull(impactedService); - Objects.requireNonNull(ttim); - Objects.requireNonNull(guid); } } From bc25d3f091150223da83f26ab207dc46b9a654b7 Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Tue, 28 Jul 2026 12:35:06 -0500 Subject: [PATCH 33/60] Refactor ArrivalMapper and TrainLocationsMapper; update package structure and enhance mapping methods for LocationTrain --- .../internal/mapper/LocationTrainMapper.java | 32 -------- .../internal/mapper/TrainLocationsMapper.java | 21 ++++- .../ArrivalMapperTest.java | 2 +- .../location/TrainLocationsMapperTest.java | 80 +++++++++++++++++++ 4 files changed, 101 insertions(+), 34 deletions(-) delete mode 100644 src/main/java/com/cta4j/train/location/internal/mapper/LocationTrainMapper.java rename src/test/java/com/cta4j/train/{arrival => common}/ArrivalMapperTest.java (98%) diff --git a/src/main/java/com/cta4j/train/location/internal/mapper/LocationTrainMapper.java b/src/main/java/com/cta4j/train/location/internal/mapper/LocationTrainMapper.java deleted file mode 100644 index a02ab452..00000000 --- a/src/main/java/com/cta4j/train/location/internal/mapper/LocationTrainMapper.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.cta4j.train.location.internal.mapper; - -import com.cta4j.train.common.internal.mapper.Qualifiers; -import com.cta4j.train.location.internal.wire.CtaLocationTrain; -import com.cta4j.train.location.model.LocationTrain; -import org.jetbrains.annotations.ApiStatus; -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.factory.Mappers; - -@Mapper(uses = Qualifiers.class) -@ApiStatus.Internal -public interface LocationTrainMapper { - LocationTrainMapper INSTANCE = Mappers.getMapper(LocationTrainMapper.class); - - @Mapping(target = "run", source = "rn") - @Mapping(target = "destinationStationId", source = "destSt") - @Mapping(target = "destinationName", source = "destNm") - @Mapping(target = "direction", source = "trDr", qualifiedByName = "map15ToTrainDirection") - @Mapping(target = "nextStationId", source = "nextStaId") - @Mapping(target = "nextStopId", source = "nextStpId") - @Mapping(target = "nextStationName", source = "nextStaNm") - @Mapping(target = "predictionTime", source = "prdt", qualifiedByName = "mapTimestamp") - @Mapping(target = "arrivalTime", source = "arrT", qualifiedByName = "mapTimestamp") - @Mapping(target = "approaching", source = "isApp", qualifiedByName = "map01ToBoolean") - @Mapping(target = "delayed", source = "isDly", qualifiedByName = "map01ToBoolean") - @Mapping(target = "flags", source = "flags") - @Mapping(target = "coordinates.latitude", source = "lat", qualifiedByName = "mapCoordinate") - @Mapping(target = "coordinates.longitude", source = "lon", qualifiedByName = "mapCoordinate") - @Mapping(target = "coordinates.heading", source = "heading", qualifiedByName = "mapHeading") - LocationTrain toDomain(CtaLocationTrain train); -} diff --git a/src/main/java/com/cta4j/train/location/internal/mapper/TrainLocationsMapper.java b/src/main/java/com/cta4j/train/location/internal/mapper/TrainLocationsMapper.java index cf88b5e4..135b94d4 100644 --- a/src/main/java/com/cta4j/train/location/internal/mapper/TrainLocationsMapper.java +++ b/src/main/java/com/cta4j/train/location/internal/mapper/TrainLocationsMapper.java @@ -1,7 +1,9 @@ package com.cta4j.train.location.internal.mapper; import com.cta4j.train.common.internal.mapper.Qualifiers; +import com.cta4j.train.location.internal.wire.CtaLocationTrain; import com.cta4j.train.location.internal.wire.CtaRoute; +import com.cta4j.train.location.model.LocationTrain; import com.cta4j.train.location.model.TrainLocations; import org.jetbrains.annotations.ApiStatus; import org.mapstruct.Mapper; @@ -10,7 +12,7 @@ import org.mapstruct.factory.Mappers; @Mapper( - uses = {Qualifiers.class, LocationTrainMapper.class}, + uses = Qualifiers.class, nullValueIterableMappingStrategy = NullValueMappingStrategy.RETURN_DEFAULT ) @ApiStatus.Internal @@ -20,4 +22,21 @@ public interface TrainLocationsMapper { @Mapping(target = "line", source = "name", qualifiedByName = "mapLine") @Mapping(target = "trains", source = "train") TrainLocations toDomain(CtaRoute route); + + @Mapping(target = "run", source = "rn") + @Mapping(target = "destinationStationId", source = "destSt") + @Mapping(target = "destinationName", source = "destNm") + @Mapping(target = "direction", source = "trDr", qualifiedByName = "map15ToTrainDirection") + @Mapping(target = "nextStationId", source = "nextStaId") + @Mapping(target = "nextStopId", source = "nextStpId") + @Mapping(target = "nextStationName", source = "nextStaNm") + @Mapping(target = "predictionTime", source = "prdt", qualifiedByName = "mapTimestamp") + @Mapping(target = "arrivalTime", source = "arrT", qualifiedByName = "mapTimestamp") + @Mapping(target = "approaching", source = "isApp", qualifiedByName = "map01ToBoolean") + @Mapping(target = "delayed", source = "isDly", qualifiedByName = "map01ToBoolean") + @Mapping(target = "flags", source = "flags") + @Mapping(target = "coordinates.latitude", source = "lat", qualifiedByName = "mapCoordinate") + @Mapping(target = "coordinates.longitude", source = "lon", qualifiedByName = "mapCoordinate") + @Mapping(target = "coordinates.heading", source = "heading", qualifiedByName = "mapHeading") + LocationTrain toDomain(CtaLocationTrain train); } diff --git a/src/test/java/com/cta4j/train/arrival/ArrivalMapperTest.java b/src/test/java/com/cta4j/train/common/ArrivalMapperTest.java similarity index 98% rename from src/test/java/com/cta4j/train/arrival/ArrivalMapperTest.java rename to src/test/java/com/cta4j/train/common/ArrivalMapperTest.java index 71ab7c35..3d0f2fad 100644 --- a/src/test/java/com/cta4j/train/arrival/ArrivalMapperTest.java +++ b/src/test/java/com/cta4j/train/common/ArrivalMapperTest.java @@ -1,4 +1,4 @@ -package com.cta4j.train.arrival; +package com.cta4j.train.common; import com.cta4j.train.common.internal.mapper.ArrivalMapper; import com.cta4j.train.common.internal.wire.CtaArrival; diff --git a/src/test/java/com/cta4j/train/location/TrainLocationsMapperTest.java b/src/test/java/com/cta4j/train/location/TrainLocationsMapperTest.java index edb5ba14..8cb28e8c 100644 --- a/src/test/java/com/cta4j/train/location/TrainLocationsMapperTest.java +++ b/src/test/java/com/cta4j/train/location/TrainLocationsMapperTest.java @@ -1,9 +1,11 @@ package com.cta4j.train.location; +import com.cta4j.train.common.model.TrainDirection; import com.cta4j.train.common.model.TrainLine; import com.cta4j.train.location.internal.mapper.TrainLocationsMapper; import com.cta4j.train.location.internal.wire.CtaLocationTrain; import com.cta4j.train.location.internal.wire.CtaRoute; +import com.cta4j.train.location.model.LocationTrain; import com.cta4j.train.location.model.TrainLocations; import org.junit.jupiter.api.Test; @@ -40,4 +42,82 @@ void toDomain_mapsNullTrainListAsEmpty() { assertThat(locations.trains()).isEmpty(); } + + @Test + void toDomain_train_mapsAllFields() { + CtaLocationTrain wire = new CtaLocationTrain( + "123", "30077", "O'Hare", "1", + "40100", "30070", "Howard", + "2015-04-30T20:23:53", + "2015-04-30T20:25:00", + "0", "0", "some-flag", + "41.88", "-87.63", "180" + ); + + LocationTrain train = TrainLocationsMapper.INSTANCE.toDomain(wire); + + assertThat(train.run()).isEqualTo("123"); + assertThat(train.destinationStationId()).isEqualTo("30077"); + assertThat(train.destinationName()).isEqualTo("O'Hare"); + assertThat(train.direction()).isEqualTo(TrainDirection.NORTHBOUND); + assertThat(train.nextStationId()).isEqualTo("40100"); + assertThat(train.nextStopId()).isEqualTo("30070"); + assertThat(train.nextStationName()).isEqualTo("Howard"); + assertThat(train.approaching()).isFalse(); + assertThat(train.delayed()).isFalse(); + assertThat(train.flags()).isEqualTo("some-flag"); + assertThat(train.coordinates()).isNotNull(); + assertThat(train.coordinates().latitude()).isEqualByComparingTo("41.88"); + assertThat(train.coordinates().longitude()).isEqualByComparingTo("-87.63"); + assertThat(train.coordinates().heading()).isEqualTo(180); + } + + @Test + void toDomain_train_mapsSouthboundDirection() { + CtaLocationTrain wire = new CtaLocationTrain( + "123", "30077", "O'Hare", "5", + "40100", "30070", "Howard", + "2015-04-30T20:23:53", + "2015-04-30T20:25:00", + "0", "0", null, + "41.88", "-87.63", "180" + ); + + LocationTrain train = TrainLocationsMapper.INSTANCE.toDomain(wire); + + assertThat(train.direction()).isEqualTo(TrainDirection.SOUTHBOUND); + } + + @Test + void toDomain_train_mapsApproachingAndDelayedTrue() { + CtaLocationTrain wire = new CtaLocationTrain( + "123", "30077", "O'Hare", "1", + "40100", "30070", "Howard", + "2015-04-30T20:23:53", + "2015-04-30T20:25:00", + "1", "1", null, + "41.88", "-87.63", "180" + ); + + LocationTrain train = TrainLocationsMapper.INSTANCE.toDomain(wire); + + assertThat(train.approaching()).isTrue(); + assertThat(train.delayed()).isTrue(); + } + + @Test + void toDomain_train_mapsNullFlagsAsNull() { + CtaLocationTrain wire = new CtaLocationTrain( + "123", "30077", "O'Hare", "1", + "40100", "30070", "Howard", + "2015-04-30T20:23:53", + "2015-04-30T20:25:00", + "0", "0", null, + "41.88", "-87.63", "180" + ); + + LocationTrain train = TrainLocationsMapper.INSTANCE.toDomain(wire); + + assertThat(train.flags()).isNull(); + } } From 7a816c04ce3d1a5d794e158fca7f8e38208a757a Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Tue, 28 Jul 2026 16:08:42 -0500 Subject: [PATCH 34/60] Add tests for DetailedAlertsApiImpl and AlertMapper; validate alert mapping and query parameters --- .../alert/common/AlertQualifiersTest.java | 77 +++ .../internal/impl/AlertApiImplTest.java | 8 + .../common/model/AlertTrainLineTest.java | 51 ++ .../alert/detailedalert/AlertMapperTest.java | 181 ++++++++ .../DetailedAlertsApiImplTest.java | 438 ++++++++++++++++++ .../Cta4jDetailedAlertsExceptionTest.java | 43 ++ .../DetailedAlertsErrorCodeTest.java | 26 ++ .../internal/wire/CtaAlertsTest.java | 59 +++ .../wire/CtaImpactedServicesTest.java | 30 ++ .../detailedalert/model/SeverityTest.java | 33 ++ .../detailedalert/query/AlertsQueryTest.java | 73 +++ .../query/BusRouteAlertsQueryTest.java | 79 ++++ .../query/LineAlertsQueryTest.java | 81 ++++ .../query/StationAlertsQueryTest.java | 78 ++++ .../internal/util/BooleanParserTest.java | 38 ++ .../internal/util/TimestampParserTest.java | 73 +++ .../alert/detailedalert/bad_error_code.json | 7 + .../detailedalert/empty_alert_array.json | 8 + .../detailedalert/error_message_absent.json | 6 + .../detailedalert/error_message_blank.json | 7 + .../alert/detailedalert/fatal_error.json | 7 + .../alert/detailedalert/list_success.json | 104 +++++ .../alert/detailedalert/no_active_alerts.json | 7 + .../no_active_alerts_for_filter.json | 7 + .../alert/detailedalert/ok_no_alerts.json | 6 + 25 files changed, 1527 insertions(+) create mode 100644 src/test/java/com/cta4j/alert/common/model/AlertTrainLineTest.java create mode 100644 src/test/java/com/cta4j/alert/detailedalert/AlertMapperTest.java create mode 100644 src/test/java/com/cta4j/alert/detailedalert/DetailedAlertsApiImplTest.java create mode 100644 src/test/java/com/cta4j/alert/detailedalert/exception/Cta4jDetailedAlertsExceptionTest.java create mode 100644 src/test/java/com/cta4j/alert/detailedalert/exception/DetailedAlertsErrorCodeTest.java create mode 100644 src/test/java/com/cta4j/alert/detailedalert/internal/wire/CtaAlertsTest.java create mode 100644 src/test/java/com/cta4j/alert/detailedalert/internal/wire/CtaImpactedServicesTest.java create mode 100644 src/test/java/com/cta4j/alert/detailedalert/model/SeverityTest.java create mode 100644 src/test/java/com/cta4j/alert/detailedalert/query/AlertsQueryTest.java create mode 100644 src/test/java/com/cta4j/alert/detailedalert/query/BusRouteAlertsQueryTest.java create mode 100644 src/test/java/com/cta4j/alert/detailedalert/query/LineAlertsQueryTest.java create mode 100644 src/test/java/com/cta4j/alert/detailedalert/query/StationAlertsQueryTest.java create mode 100644 src/test/java/com/cta4j/common/internal/util/BooleanParserTest.java create mode 100644 src/test/java/com/cta4j/common/internal/util/TimestampParserTest.java create mode 100644 src/test/resources/alert/detailedalert/bad_error_code.json create mode 100644 src/test/resources/alert/detailedalert/empty_alert_array.json create mode 100644 src/test/resources/alert/detailedalert/error_message_absent.json create mode 100644 src/test/resources/alert/detailedalert/error_message_blank.json create mode 100644 src/test/resources/alert/detailedalert/fatal_error.json create mode 100644 src/test/resources/alert/detailedalert/list_success.json create mode 100644 src/test/resources/alert/detailedalert/no_active_alerts.json create mode 100644 src/test/resources/alert/detailedalert/no_active_alerts_for_filter.json create mode 100644 src/test/resources/alert/detailedalert/ok_no_alerts.json diff --git a/src/test/java/com/cta4j/alert/common/AlertQualifiersTest.java b/src/test/java/com/cta4j/alert/common/AlertQualifiersTest.java index e1982426..53f3fc95 100644 --- a/src/test/java/com/cta4j/alert/common/AlertQualifiersTest.java +++ b/src/test/java/com/cta4j/alert/common/AlertQualifiersTest.java @@ -1,9 +1,11 @@ package com.cta4j.alert.common; import com.cta4j.alert.common.internal.mapper.Qualifiers; +import com.cta4j.alert.common.model.ServiceType; import org.junit.jupiter.api.Test; import java.net.URI; +import java.time.Instant; import static org.assertj.core.api.Assertions.*; @@ -21,4 +23,79 @@ void mapUri_throwsIllegalArgumentException_whenValueIsInvalid() { .withMessageContaining("Failed to parse URI") .withCauseInstanceOf(java.net.URISyntaxException.class); } + + @Test + void mapTimestamp_returnsInstant_atChicagoZone_whenTimeOfDayIsPresent() { + Instant result = Qualifiers.mapTimestamp("2026-07-01T05:00:00"); + + // America/Chicago is UTC-5 (CDT) in July. + assertThat(result).isEqualTo(Instant.parse("2026-07-01T10:00:00Z")); + } + + @Test + void mapTimestamp_defaultsToMidnight_whenTimeOfDayIsAbsent() { + // Confirmed against the live Detailed Alerts API: EventStart/EventEnd are sometimes returned as a + // bare date (e.g., "2027-09-30") with no time-of-day component. + Instant result = Qualifiers.mapTimestamp("2027-09-30"); + + assertThat(result).isEqualTo(Instant.parse("2027-09-30T05:00:00Z")); + } + + @Test + void mapTimestamp_appliesStandardTimeOffset_whenDateIsOutsideDaylightSavingTime() { + Instant result = Qualifiers.mapTimestamp("2025-11-07"); + + assertThat(result).isEqualTo(Instant.parse("2025-11-07T06:00:00Z")); + } + + @Test + void mapTimestamp_returnsNull_whenValueIsNull() { + assertThat(Qualifiers.mapTimestamp(null)).isNull(); + } + + @Test + void mapTimestamp_throwsIllegalArgumentException_whenValueIsInvalid() { + assertThatIllegalArgumentException().isThrownBy(() -> Qualifiers.mapTimestamp("not-a-timestamp")); + } + + @Test + void map01ToBoolean_returnsFalse_whenValueIsZero() { + assertThat(Qualifiers.map01ToBoolean("0")).isFalse(); + } + + @Test + void map01ToBoolean_returnsTrue_whenValueIsOne() { + assertThat(Qualifiers.map01ToBoolean("1")).isTrue(); + } + + @Test + void map01ToBoolean_throwsIllegalArgumentException_whenValueIsInvalid() { + assertThatIllegalArgumentException().isThrownBy(() -> Qualifiers.map01ToBoolean("2")); + } + + @Test + void mapScore_returnsParsedInt() { + assertThat(Qualifiers.mapScore("37")).isEqualTo(37); + } + + @Test + void mapScore_throwsIllegalArgumentException_whenValueIsNotNumeric() { + assertThatIllegalArgumentException().isThrownBy(() -> Qualifiers.mapScore("not-a-number")) + .withMessageContaining("Failed to parse score: not-a-number") + .withCauseInstanceOf(NumberFormatException.class); + } + + @Test + void mapServiceType_returnsCorrectValue_forEachKnownCode() { + assertThat(Qualifiers.mapServiceType("B")).isEqualTo(ServiceType.BUS); + assertThat(Qualifiers.mapServiceType("R")).isEqualTo(ServiceType.RAIL); + assertThat(Qualifiers.mapServiceType("T")).isEqualTo(ServiceType.STATION); + assertThat(Qualifiers.mapServiceType("X")).isEqualTo(ServiceType.SYSTEMWIDE); + } + + @Test + void mapServiceType_throwsIllegalArgumentException_whenCodeIsUnknown() { + assertThatIllegalArgumentException().isThrownBy(() -> Qualifiers.mapServiceType("Z")) + .withMessageContaining("Unknown service type: Z"); + } } diff --git a/src/test/java/com/cta4j/alert/common/internal/impl/AlertApiImplTest.java b/src/test/java/com/cta4j/alert/common/internal/impl/AlertApiImplTest.java index 6ceb3b9f..441fe0a7 100644 --- a/src/test/java/com/cta4j/alert/common/internal/impl/AlertApiImplTest.java +++ b/src/test/java/com/cta4j/alert/common/internal/impl/AlertApiImplTest.java @@ -2,6 +2,7 @@ import com.cta4j.alert.AlertApi; import com.cta4j.alert.common.internal.config.AlertApiConfig; +import com.cta4j.alert.detailedalert.DetailedAlertsApi; import com.cta4j.alert.routestatus.RouteStatusApi; import com.github.tomakehurst.wiremock.WireMockServer; import org.junit.jupiter.api.AfterEach; @@ -40,6 +41,13 @@ void routeStatus_returnsNonNull() { assertThat(result).isNotNull(); } + @Test + void detailedAlerts_returnsNonNull() { + DetailedAlertsApi result = this.api.detailedAlerts(); + + assertThat(result).isNotNull(); + } + @Test void builderImpl_host_throwsNullPointerException_whenHostIsNull() { AlertApiImpl.BuilderImpl builder = new AlertApiImpl.BuilderImpl(); diff --git a/src/test/java/com/cta4j/alert/common/model/AlertTrainLineTest.java b/src/test/java/com/cta4j/alert/common/model/AlertTrainLineTest.java new file mode 100644 index 00000000..3743f117 --- /dev/null +++ b/src/test/java/com/cta4j/alert/common/model/AlertTrainLineTest.java @@ -0,0 +1,51 @@ +package com.cta4j.alert.common.model; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.*; + +class AlertTrainLineTest { + @Test + void fromCode_returnsCorrectValue_forEveryDefinedCode() { + for (AlertTrainLine line : AlertTrainLine.values()) { + assertThat(AlertTrainLine.fromCode(line.getCode())).isEqualTo(line); + } + } + + @Test + void fromCode_isCaseInsensitive() { + assertThat(AlertTrainLine.fromCode("red")).isEqualTo(AlertTrainLine.RED); + assertThat(AlertTrainLine.fromCode("PEXP")).isEqualTo(AlertTrainLine.PURPLE_EXPRESS); + assertThat(AlertTrainLine.fromCode("pexp")).isEqualTo(AlertTrainLine.PURPLE_EXPRESS); + } + + @Test + void fromCode_distinguishesPurpleFromPurpleExpress() { + assertThat(AlertTrainLine.fromCode("P")).isEqualTo(AlertTrainLine.PURPLE); + assertThat(AlertTrainLine.fromCode("Pexp")).isEqualTo(AlertTrainLine.PURPLE_EXPRESS); + assertThat(AlertTrainLine.PURPLE).isNotEqualTo(AlertTrainLine.PURPLE_EXPRESS); + } + + @Test + void fromCode_throwsIllegalArgumentException_whenCodeIsUnknown() { + assertThatIllegalArgumentException().isThrownBy(() -> AlertTrainLine.fromCode("Unknown")) + .withMessageContaining("Invalid alert train line: Unknown"); + } + + @Test + void fromCode_throwsNullPointerException_whenCodeIsNull() { + assertThatNullPointerException().isThrownBy(() -> AlertTrainLine.fromCode(null)); + } + + @Test + void getCode_returnsCode() { + assertThat(AlertTrainLine.RED.getCode()).isEqualTo("Red"); + assertThat(AlertTrainLine.BROWN.getCode()).isEqualTo("Brn"); + assertThat(AlertTrainLine.GREEN.getCode()).isEqualTo("G"); + assertThat(AlertTrainLine.ORANGE.getCode()).isEqualTo("Org"); + assertThat(AlertTrainLine.PURPLE.getCode()).isEqualTo("P"); + assertThat(AlertTrainLine.PURPLE_EXPRESS.getCode()).isEqualTo("Pexp"); + assertThat(AlertTrainLine.PINK.getCode()).isEqualTo("Pink"); + assertThat(AlertTrainLine.YELLOW.getCode()).isEqualTo("Y"); + } +} \ No newline at end of file diff --git a/src/test/java/com/cta4j/alert/detailedalert/AlertMapperTest.java b/src/test/java/com/cta4j/alert/detailedalert/AlertMapperTest.java new file mode 100644 index 00000000..8150be0d --- /dev/null +++ b/src/test/java/com/cta4j/alert/detailedalert/AlertMapperTest.java @@ -0,0 +1,181 @@ +package com.cta4j.alert.detailedalert; + +import com.cta4j.alert.common.internal.wire.CtaCdata; +import com.cta4j.alert.common.model.ServiceType; +import com.cta4j.alert.detailedalert.internal.mapper.AlertMapper; +import com.cta4j.alert.detailedalert.internal.wire.CtaAlert; +import com.cta4j.alert.detailedalert.internal.wire.CtaImpactedService; +import com.cta4j.alert.detailedalert.internal.wire.CtaImpactedServices; +import com.cta4j.alert.detailedalert.model.Alert; +import com.cta4j.alert.detailedalert.model.ImpactedService; +import org.junit.jupiter.api.Test; + +import java.time.Instant; +import java.util.List; + +import static org.assertj.core.api.Assertions.*; + +class AlertMapperTest { + @Test + void toDomain_mapsAllFields_whenOptionalFieldsArePresent() { + CtaImpactedService wireService = new CtaImpactedService( + "B", "Bus Route", "Clark", "22", "565a5c", "ffffff", + new CtaCdata("http://www.transitchicago.com/bus/22/") + ); + + CtaAlert wire = new CtaAlert( + "115070", + "Route 22 Rerouted", + "Route 22 is rerouted due to construction", + new CtaCdata("Route 22 buses are being rerouted due to construction on Clark St."), + "37", + "06c", + "planned", + "Planned Reroute", + "2026-07-01T05:00:00", + "2026-08-01T05:00:00", + "0", + "0", + new CtaCdata("http://www.transitchicago.com/alerts/115070"), + new CtaImpactedServices(List.of(wireService)), + "0", + "664b81c1-197b-450b-a00c-090483b90bb9" + ); + + Alert alert = AlertMapper.INSTANCE.toDomain(wire); + + assertThat(alert.id()).isEqualTo("115070"); + assertThat(alert.headline()).isEqualTo("Route 22 Rerouted"); + assertThat(alert.shortDescription()).isEqualTo("Route 22 is rerouted due to construction"); + assertThat(alert.fullDescription()) + .isEqualTo("Route 22 buses are being rerouted due to construction on Clark St."); + assertThat(alert.severity().score()).isEqualTo(37); + assertThat(alert.severity().color()).isEqualTo("06c"); + assertThat(alert.severity().css()).isEqualTo("planned"); + assertThat(alert.impact()).isEqualTo("Planned Reroute"); + assertThat(alert.startTime()).isEqualTo(Instant.parse("2026-07-01T10:00:00Z")); + assertThat(alert.endTime()).isEqualTo(Instant.parse("2026-08-01T10:00:00Z")); + assertThat(alert.openEnded()).isFalse(); + assertThat(alert.major()).isFalse(); + assertThat(alert.url()).hasToString("http://www.transitchicago.com/alerts/115070"); + assertThat(alert.impactedServices()).hasSize(1); + + ImpactedService impactedService = alert.impactedServices().getFirst(); + assertThat(impactedService.type()).isEqualTo(ServiceType.BUS); + assertThat(impactedService.typeDescription()).isEqualTo("Bus Route"); + assertThat(impactedService.name()).isEqualTo("Clark"); + assertThat(impactedService.serviceId()).isEqualTo("22"); + assertThat(impactedService.color()).isEqualTo("565a5c"); + assertThat(impactedService.textColor()).isEqualTo("ffffff"); + assertThat(impactedService.url()).hasToString("http://www.transitchicago.com/bus/22/"); + + assertThat(alert.ttim()).isEqualTo("0"); + assertThat(alert.guid()).isEqualTo("664b81c1-197b-450b-a00c-090483b90bb9"); + } + + @Test + void toDomain_mapsNullEndTimeTtimAndGuid_whenAbsent() { + CtaImpactedService wireService = new CtaImpactedService( + "T", "Train Station", "Austin", "41260", "009b3a", "FFFFFF", + new CtaCdata("http://www.transitchicago.com/travel_information/station.aspx?StopId=24") + ); + + CtaAlert wire = new CtaAlert( + "115080", + "Austin Main Stationhouse Temporarily Closed", + "Elevator out of service", + new CtaCdata("The elevator at Austin is out of service."), + "9", + "000000", + "minor", + "Elevator Status", + "2026-07-15T00:00:00", + null, + "1", + "1", + new CtaCdata("http://www.transitchicago.com/alerts/115080"), + new CtaImpactedServices(List.of(wireService)), + null, + null + ); + + Alert alert = AlertMapper.INSTANCE.toDomain(wire); + + assertThat(alert.endTime()).isNull(); + assertThat(alert.openEnded()).isTrue(); + assertThat(alert.major()).isTrue(); + assertThat(alert.ttim()).isNull(); + assertThat(alert.guid()).isNull(); + } + + @Test + void toDomain_mapsDateOnlyEventStartAndEventEnd_toMidnightChicagoTime() { + // Confirmed against the live Detailed Alerts API: EventStart/EventEnd are sometimes a bare + // "yyyy-MM-dd" date with no time-of-day component (e.g., long-running planned service changes). + CtaImpactedService wireService = new CtaImpactedService( + "B", "Bus Route", "South Pulaski", "53A", "059", "ffffff", + new CtaCdata("http://www.transitchicago.com/riding_cta/bus_route.aspx?RouteId=207") + ); + + CtaAlert wire = new CtaAlert( + "115090", + "Later, More Frequent Weekend Service", + "Service is being increased on the South Pulaski corridor.", + new CtaCdata("Later evening and more frequent weekend service."), + "11", + "000000", + "normal", + "Added Service", + "2025-11-07", + "2027-09-30", + "0", + "0", + new CtaCdata("http://www.transitchicago.com/alerts/115090"), + new CtaImpactedServices(List.of(wireService)), + "0", + "9979cd0c-a29d-4b52-805d-4baa0b32322b" + ); + + Alert alert = AlertMapper.INSTANCE.toDomain(wire); + + assertThat(alert.startTime()).isEqualTo(Instant.parse("2025-11-07T06:00:00Z")); + assertThat(alert.endTime()).isEqualTo(Instant.parse("2027-09-30T05:00:00Z")); + } + + @Test + void toDomain_mapsMultipleImpactedServices() { + CtaImpactedService station = new CtaImpactedService( + "T", "Train Station", "Austin", "41260", "009b3a", "FFFFFF", + new CtaCdata("http://www.transitchicago.com/travel_information/station.aspx?StopId=24") + ); + CtaImpactedService route = new CtaImpactedService( + "R", "Train Route", "Green Line", "G", "009b3a", "FFFFFF", + new CtaCdata("http://www.transitchicago.com/greenline/") + ); + + CtaAlert wire = new CtaAlert( + "115080", + "Austin Main Stationhouse Temporarily Closed", + "Elevator out of service", + new CtaCdata("The elevator at Austin is out of service."), + "9", + "000000", + "minor", + "Elevator Status", + "2026-07-15T00:00:00", + null, + "1", + "0", + new CtaCdata("http://www.transitchicago.com/alerts/115080"), + new CtaImpactedServices(List.of(station, route)), + "1", + "d41b2532-09ca-4827-b9c6-f4299cc86fb6" + ); + + Alert alert = AlertMapper.INSTANCE.toDomain(wire); + + assertThat(alert.impactedServices()).hasSize(2); + assertThat(alert.impactedServices().get(0).type()).isEqualTo(ServiceType.STATION); + assertThat(alert.impactedServices().get(1).type()).isEqualTo(ServiceType.RAIL); + } +} \ No newline at end of file diff --git a/src/test/java/com/cta4j/alert/detailedalert/DetailedAlertsApiImplTest.java b/src/test/java/com/cta4j/alert/detailedalert/DetailedAlertsApiImplTest.java new file mode 100644 index 00000000..32cce1d0 --- /dev/null +++ b/src/test/java/com/cta4j/alert/detailedalert/DetailedAlertsApiImplTest.java @@ -0,0 +1,438 @@ +package com.cta4j.alert.detailedalert; + +import com.cta4j.TestFixtures; +import com.cta4j.alert.common.internal.config.AlertApiConfig; +import com.cta4j.alert.common.internal.util.AlertApiConstants; +import com.cta4j.alert.common.model.AlertTrainLine; +import com.cta4j.alert.detailedalert.exception.Cta4jDetailedAlertsException; +import com.cta4j.alert.detailedalert.exception.DetailedAlertsErrorCode; +import com.cta4j.alert.detailedalert.internal.impl.DetailedAlertsApiImpl; +import com.cta4j.alert.detailedalert.model.Alert; +import com.cta4j.alert.detailedalert.query.AlertsQuery; +import com.github.tomakehurst.wiremock.WireMockServer; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import tools.jackson.core.JacksonException; + +import java.time.LocalDate; +import java.util.Arrays; +import java.util.List; + +import static com.github.tomakehurst.wiremock.client.WireMock.*; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; +import static org.assertj.core.api.Assertions.*; + +class DetailedAlertsApiImplTest { + private WireMockServer server; + private DetailedAlertsApiImpl api; + + @BeforeEach + void setUp() { + this.server = new WireMockServer(wireMockConfig().dynamicPort()); + this.server.start(); + AlertApiConfig config = new AlertApiConfig("http", "localhost", this.server.port()); + this.api = new DetailedAlertsApiImpl(config); + } + + @AfterEach + void tearDown() { + this.server.stop(); + } + + @Test + void list_returnsAlerts_whenResponseContainsData() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/alerts.aspx")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("alert/detailedalert/list_success.json")))); + + List alerts = this.api.list(); + + assertThat(alerts).hasSize(3); + + Alert busReroute = alerts.getFirst(); + assertThat(busReroute.id()).isEqualTo("115070"); + assertThat(busReroute.headline()).isEqualTo("Route 22 Rerouted"); + assertThat(busReroute.severity().score()).isEqualTo(37); + assertThat(busReroute.endTime()).isNotNull(); + assertThat(busReroute.openEnded()).isFalse(); + assertThat(busReroute.major()).isFalse(); + assertThat(busReroute.impactedServices()).hasSize(1); + assertThat(busReroute.ttim()).isEqualTo("0"); + assertThat(busReroute.guid()).isEqualTo("664b81c1-197b-450b-a00c-090483b90bb9"); + + Alert stationClosure = alerts.get(1); + assertThat(stationClosure.id()).isEqualTo("115080"); + assertThat(stationClosure.endTime()).isNull(); + assertThat(stationClosure.openEnded()).isTrue(); + assertThat(stationClosure.major()).isFalse(); + assertThat(stationClosure.impactedServices()).hasSize(2); + assertThat(stationClosure.ttim()).isEqualTo("1"); + assertThat(stationClosure.guid()).isEqualTo("d41b2532-09ca-4827-b9c6-f4299cc86fb6"); + + // Confirmed against the live API: EventStart/EventEnd are sometimes bare dates with no time-of-day. + Alert dateOnlyAlert = alerts.get(2); + assertThat(dateOnlyAlert.id()).isEqualTo("115090"); + assertThat(dateOnlyAlert.startTime()).isNotNull(); + assertThat(dateOnlyAlert.endTime()).isNotNull(); + } + + @Test + void list_sendsDefaultQueryParameters() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/alerts.aspx")) + .withQueryParam("activeonly", equalTo("false")) + .withQueryParam("accessibility", equalTo("true")) + .withQueryParam("planned", equalTo("true")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("alert/detailedalert/list_success.json")))); + + List alerts = this.api.list(); + + assertThat(alerts).hasSize(3); + this.server.verify(getRequestedFor(urlPathEqualTo("/api/1.0/alerts.aspx")) + .withoutQueryParam("bystartdate") + .withoutQueryParam("recentdays")); + } + + @Test + void list_sendsByStartDateParameter_whenProvided() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/alerts.aspx")) + .withQueryParam("bystartdate", equalTo("20260701")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("alert/detailedalert/list_success.json")))); + + AlertsQuery query = AlertsQuery.builder() + .byStartDate(LocalDate.of(2026, 7, 1)) + .build(); + + List alerts = this.api.list(query); + + assertThat(alerts).hasSize(3); + } + + @Test + void list_sendsRecentDaysParameter_whenProvided() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/alerts.aspx")) + .withQueryParam("recentdays", equalTo("7")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("alert/detailedalert/list_success.json")))); + + AlertsQuery query = AlertsQuery.builder() + .recentDays(7) + .build(); + + List alerts = this.api.list(query); + + assertThat(alerts).hasSize(3); + } + + @Test + void list_returnsEmpty_whenErrorCodeIsNoActiveAlerts() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/alerts.aspx")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("alert/detailedalert/no_active_alerts.json")))); + + List alerts = this.api.list(); + + assertThat(alerts).isEmpty(); + } + + @Test + void list_returnsEmpty_whenErrorCodeIsNoActiveAlertsForFilter() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/alerts.aspx")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("alert/detailedalert/no_active_alerts_for_filter.json")))); + + List alerts = this.api.list(); + + assertThat(alerts).isEmpty(); + } + + @Test + void list_returnsEmpty_whenAlertIsExplicitlyEmptyArray() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/alerts.aspx")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("alert/detailedalert/empty_alert_array.json")))); + + List alerts = this.api.list(); + + assertThat(alerts).isEmpty(); + } + + @Test + void list_returnsEmpty_whenErrorCodeIsOk_andNoAlertData() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/alerts.aspx")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("alert/detailedalert/ok_no_alerts.json")))); + + List alerts = this.api.list(); + + assertThat(alerts).isEmpty(); + } + + @Test + void list_throwsCta4jDetailedAlertsException_whenResponseContainsFatalError() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/alerts.aspx")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("alert/detailedalert/fatal_error.json")))); + + assertThatThrownBy(() -> this.api.list()) + .isInstanceOf(Cta4jDetailedAlertsException.class) + .hasMessage("Invalid option for parameter 'activeonly': Valid options are 'true', 'false'") + .satisfies(e -> assertThat(((Cta4jDetailedAlertsException) e).getErrorCode()) + .isEqualTo(DetailedAlertsErrorCode.INVALID_ACTIVEONLY)) + .satisfies(e -> assertThat(((Cta4jDetailedAlertsException) e).getRawErrorCode()).isEqualTo(100)) + .satisfies(e -> assertThat(((Cta4jDetailedAlertsException) e).getEndpoint()) + .isEqualTo(AlertApiConstants.DETAILED_ALERTS_ENDPOINT)); + } + + @Test + void list_throwsCta4jDetailedAlertsException_withDefaultMessage_whenErrorMessageIsBlank() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/alerts.aspx")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("alert/detailedalert/error_message_blank.json")))); + + assertThatThrownBy(() -> this.api.list()) + .isInstanceOf(Cta4jDetailedAlertsException.class) + .hasMessage("An unknown error occurred.") + .satisfies(e -> assertThat(((Cta4jDetailedAlertsException) e).getErrorCode()) + .isEqualTo(DetailedAlertsErrorCode.SERVER_ERROR)); + } + + @Test + void list_throwsCta4jDetailedAlertsException_withDefaultMessage_whenErrorMessageIsAbsent() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/alerts.aspx")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("alert/detailedalert/error_message_absent.json")))); + + assertThatThrownBy(() -> this.api.list()) + .isInstanceOf(Cta4jDetailedAlertsException.class) + .hasMessage("An unknown error occurred.") + .satisfies(e -> assertThat(((Cta4jDetailedAlertsException) e).getErrorCode()) + .isEqualTo(DetailedAlertsErrorCode.INVALID_PARAMETER)); + } + + @Test + void list_throwsCta4jDetailedAlertsException_whenErrorCodeIsNotNumeric() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/alerts.aspx")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("alert/detailedalert/bad_error_code.json")))); + + assertThatThrownBy(() -> this.api.list()) + .isInstanceOf(Cta4jDetailedAlertsException.class) + .hasMessage("Failed to parse error code") + .satisfies(e -> assertThat(e.getCause()).isInstanceOf(NumberFormatException.class)); + } + + @Test + void list_throwsCta4jDetailedAlertsException_whenResponseIsNotJson() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/alerts.aspx")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody("not-json"))); + + assertThatThrownBy(() -> this.api.list()) + .isInstanceOf(Cta4jDetailedAlertsException.class) + .hasMessage("Failed to parse response") + .satisfies(e -> assertThat(((Cta4jDetailedAlertsException) e).getEndpoint()) + .isEqualTo(AlertApiConstants.DETAILED_ALERTS_ENDPOINT)) + .satisfies(e -> assertThat(e.getCause()).isInstanceOf(JacksonException.class)); + } + + @Test + void list_throwsCta4jDetailedAlertsException_whenServerReturnsErrorStatus() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/alerts.aspx")) + .willReturn(aResponse() + .withStatus(500))); + + assertThatThrownBy(() -> this.api.list()) + .isInstanceOf(Cta4jDetailedAlertsException.class) + .hasMessageContaining("500") + .satisfies(e -> assertThat(e.getCause()).isNotNull()); + } + + @Test + void findByBusRouteIds_returnsEmpty_whenInputIsEmpty() { + List alerts = this.api.findByBusRouteIds(List.of()); + + assertThat(alerts).isEmpty(); + this.server.verify(0, anyRequestedFor(anyUrl())); + } + + @Test + void findByBusRouteIds_sendsRouteidParameter_asCommaJoined() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/alerts.aspx")) + .withQueryParam("routeid", equalTo("22,53")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("alert/detailedalert/list_success.json")))); + + List alerts = this.api.findByBusRouteIds(List.of("22", "53")); + + assertThat(alerts).hasSize(3); + } + + @Test + void findByBusRouteIds_throwsIllegalArgumentException_whenRouteIdIsTrainLine() { + assertThatIllegalArgumentException() + .isThrownBy(() -> this.api.findByBusRouteIds(List.of("Red"))) + .withMessageContaining("Red is a train line, not a bus route"); + + this.server.verify(0, anyRequestedFor(anyUrl())); + } + + @Test + void findByBusRouteIds_throwsIllegalArgumentException_whenRouteIdIsTrainLine_caseInsensitive() { + assertThatIllegalArgumentException() + .isThrownBy(() -> this.api.findByBusRouteIds(List.of("red"))); + + this.server.verify(0, anyRequestedFor(anyUrl())); + } + + @Test + void findByBusRouteIds_throwsNullPointerException_whenRouteIdsContainsNull() { + List withNull = Arrays.asList("22", null); + + assertThatNullPointerException().isThrownBy(() -> this.api.findByBusRouteIds(withNull)); + } + + @Test + void findByBusRouteId_delegatesToFindByBusRouteIds() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/alerts.aspx")) + .withQueryParam("routeid", equalTo("22")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("alert/detailedalert/list_success.json")))); + + List alerts = this.api.findByBusRouteId("22"); + + assertThat(alerts).hasSize(3); + } + + @Test + void findByLines_returnsEmpty_whenInputIsEmpty() { + List alerts = this.api.findByLines(List.of()); + + assertThat(alerts).isEmpty(); + this.server.verify(0, anyRequestedFor(anyUrl())); + } + + @Test + void findByLines_sendsRouteidParameter_asCommaJoinedCodes() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/alerts.aspx")) + .withQueryParam("routeid", equalTo("Red,Blue")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("alert/detailedalert/list_success.json")))); + + List alerts = this.api.findByLines(List.of(AlertTrainLine.RED, AlertTrainLine.BLUE)); + + assertThat(alerts).hasSize(3); + } + + @Test + void findByLines_sendsRouteidParameter_usingPurpleExpressCode() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/alerts.aspx")) + .withQueryParam("routeid", equalTo("Pexp")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("alert/detailedalert/list_success.json")))); + + List alerts = this.api.findByLine(AlertTrainLine.PURPLE_EXPRESS); + + assertThat(alerts).hasSize(3); + } + + @Test + void findByLines_throwsNullPointerException_whenLinesContainsNull() { + List withNull = Arrays.asList(AlertTrainLine.RED, null); + + assertThatNullPointerException().isThrownBy(() -> this.api.findByLines(withNull)); + } + + @Test + void findByLine_delegatesToFindByLines() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/alerts.aspx")) + .withQueryParam("routeid", equalTo("Red")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("alert/detailedalert/list_success.json")))); + + List alerts = this.api.findByLine(AlertTrainLine.RED); + + assertThat(alerts).hasSize(3); + } + + @Test + void findByStationIds_returnsEmpty_whenInputIsEmpty() { + List alerts = this.api.findByStationIds(List.of()); + + assertThat(alerts).isEmpty(); + this.server.verify(0, anyRequestedFor(anyUrl())); + } + + @Test + void findByStationIds_sendsStationidParameter_asCommaJoined() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/alerts.aspx")) + .withQueryParam("stationid", equalTo("40380,41260")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("alert/detailedalert/list_success.json")))); + + List alerts = this.api.findByStationIds(List.of("40380", "41260")); + + assertThat(alerts).hasSize(3); + } + + @Test + void findByStationIds_throwsNullPointerException_whenStationIdsContainsNull() { + List withNull = Arrays.asList("40380", null); + + assertThatNullPointerException().isThrownBy(() -> this.api.findByStationIds(withNull)); + } + + @Test + void findByStationId_delegatesToFindByStationIds() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/alerts.aspx")) + .withQueryParam("stationid", equalTo("41260")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("alert/detailedalert/list_success.json")))); + + List alerts = this.api.findByStationId("41260"); + + assertThat(alerts).hasSize(3); + } +} \ No newline at end of file diff --git a/src/test/java/com/cta4j/alert/detailedalert/exception/Cta4jDetailedAlertsExceptionTest.java b/src/test/java/com/cta4j/alert/detailedalert/exception/Cta4jDetailedAlertsExceptionTest.java new file mode 100644 index 00000000..ad959a52 --- /dev/null +++ b/src/test/java/com/cta4j/alert/detailedalert/exception/Cta4jDetailedAlertsExceptionTest.java @@ -0,0 +1,43 @@ +package com.cta4j.alert.detailedalert.exception; + +import com.cta4j.alert.common.internal.util.AlertApiConstants; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.*; + +class Cta4jDetailedAlertsExceptionTest { + @Test + void constructor_setsMessageEndpointAndCause_andLeavesErrorCodeNull() { + Throwable cause = new RuntimeException("root cause"); + + Cta4jDetailedAlertsException exception = new Cta4jDetailedAlertsException("Failed to parse response", cause); + + assertThat(exception.getMessage()).isEqualTo("Failed to parse response"); + assertThat(exception.getEndpoint()).isEqualTo(AlertApiConstants.DETAILED_ALERTS_ENDPOINT); + assertThat(exception.getCause()).isSameAs(cause); + assertThat(exception.getRawErrorCode()).isNull(); + assertThat(exception.getErrorCode()).isNull(); + } + + @Test + void constructor_setsMessageEndpointAndErrorCode() { + Cta4jDetailedAlertsException exception = new Cta4jDetailedAlertsException( + "Invalid option for parameter 'activeonly': Valid options are 'true', 'false'", 100 + ); + + assertThat(exception.getMessage()) + .isEqualTo("Invalid option for parameter 'activeonly': Valid options are 'true', 'false'"); + assertThat(exception.getEndpoint()).isEqualTo(AlertApiConstants.DETAILED_ALERTS_ENDPOINT); + assertThat(exception.getCause()).isNull(); + assertThat(exception.getRawErrorCode()).isEqualTo(100); + assertThat(exception.getErrorCode()).isEqualTo(DetailedAlertsErrorCode.INVALID_ACTIVEONLY); + } + + @Test + void constructor_setsUnknownErrorCode_whenRawErrorCodeIsUnrecognized() { + Cta4jDetailedAlertsException exception = new Cta4jDetailedAlertsException("Something odd happened", 999); + + assertThat(exception.getRawErrorCode()).isEqualTo(999); + assertThat(exception.getErrorCode()).isEqualTo(DetailedAlertsErrorCode.UNKNOWN); + } +} \ No newline at end of file diff --git a/src/test/java/com/cta4j/alert/detailedalert/exception/DetailedAlertsErrorCodeTest.java b/src/test/java/com/cta4j/alert/detailedalert/exception/DetailedAlertsErrorCodeTest.java new file mode 100644 index 00000000..8c825016 --- /dev/null +++ b/src/test/java/com/cta4j/alert/detailedalert/exception/DetailedAlertsErrorCodeTest.java @@ -0,0 +1,26 @@ +package com.cta4j.alert.detailedalert.exception; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.*; + +class DetailedAlertsErrorCodeTest { + @Test + void fromCode_returnsCorrectValue_forEveryDefinedCode() { + for (DetailedAlertsErrorCode code : DetailedAlertsErrorCode.values()) { + assertThat(DetailedAlertsErrorCode.fromCode(code.getCode())).isEqualTo(code); + } + } + + @Test + void fromCode_returnsUnknown_whenCodeIsUnrecognized() { + assertThat(DetailedAlertsErrorCode.fromCode(12345)).isEqualTo(DetailedAlertsErrorCode.UNKNOWN); + } + + @Test + void getCode_returnsCode() { + assertThat(DetailedAlertsErrorCode.NO_ACTIVE_ALERTS.getCode()).isEqualTo(25); + assertThat(DetailedAlertsErrorCode.NO_ACTIVE_ALERTS_FOR_FILTER.getCode()).isEqualTo(50); + assertThat(DetailedAlertsErrorCode.UNKNOWN.getCode()).isEqualTo(-1); + } +} \ No newline at end of file diff --git a/src/test/java/com/cta4j/alert/detailedalert/internal/wire/CtaAlertsTest.java b/src/test/java/com/cta4j/alert/detailedalert/internal/wire/CtaAlertsTest.java new file mode 100644 index 00000000..75adaf74 --- /dev/null +++ b/src/test/java/com/cta4j/alert/detailedalert/internal/wire/CtaAlertsTest.java @@ -0,0 +1,59 @@ +package com.cta4j.alert.detailedalert.internal.wire; + +import com.cta4j.alert.common.internal.wire.CtaCdata; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.*; + +class CtaAlertsTest { + @Test + void constructor_copiesAlert_whenNonNull() { + List alert = new ArrayList<>(List.of(newAlert("115070"))); + + CtaAlerts alerts = new CtaAlerts("2026-07-28T12:00:00", "0", null, alert); + alert.add(newAlert("115080")); + + assertThat(alerts.alert()).hasSize(1); + } + + @Test + void constructor_allowsNullAlert() { + CtaAlerts alerts = new CtaAlerts("2026-07-28T12:00:00", "25", "There are no active alerts", null); + + assertThat(alerts.alert()).isNull(); + } + + @Test + void constructor_throwsNullPointerException_whenTimestampIsNull() { + assertThatNullPointerException().isThrownBy(() -> new CtaAlerts(null, "0", null, null)); + } + + @Test + void constructor_throwsNullPointerException_whenErrorCodeIsNull() { + assertThatNullPointerException().isThrownBy(() -> new CtaAlerts("2026-07-28T12:00:00", null, null, null)); + } + + private static CtaAlert newAlert(String alertId) { + return new CtaAlert( + alertId, + "Headline", + "Short description", + new CtaCdata("Full description"), + "9", + "000000", + "minor", + "Impact", + "2026-07-01T05:00:00", + null, + "0", + "0", + new CtaCdata("http://www.transitchicago.com/alerts/" + alertId), + new CtaImpactedServices(List.of()), + null, + null + ); + } +} \ No newline at end of file diff --git a/src/test/java/com/cta4j/alert/detailedalert/internal/wire/CtaImpactedServicesTest.java b/src/test/java/com/cta4j/alert/detailedalert/internal/wire/CtaImpactedServicesTest.java new file mode 100644 index 00000000..b998c88f --- /dev/null +++ b/src/test/java/com/cta4j/alert/detailedalert/internal/wire/CtaImpactedServicesTest.java @@ -0,0 +1,30 @@ +package com.cta4j.alert.detailedalert.internal.wire; + +import com.cta4j.alert.common.internal.wire.CtaCdata; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.*; + +class CtaImpactedServicesTest { + @Test + void constructor_copiesService() { + CtaImpactedService service = new CtaImpactedService( + "B", "Bus Route", "Clark", "22", "565a5c", "ffffff", + new CtaCdata("http://www.transitchicago.com/bus/22/") + ); + List services = new ArrayList<>(List.of(service)); + + CtaImpactedServices impactedServices = new CtaImpactedServices(services); + services.add(service); + + assertThat(impactedServices.service()).hasSize(1); + } + + @Test + void constructor_throwsNullPointerException_whenServiceIsNull() { + assertThatNullPointerException().isThrownBy(() -> new CtaImpactedServices(null)); + } +} \ No newline at end of file diff --git a/src/test/java/com/cta4j/alert/detailedalert/model/SeverityTest.java b/src/test/java/com/cta4j/alert/detailedalert/model/SeverityTest.java new file mode 100644 index 00000000..3af73013 --- /dev/null +++ b/src/test/java/com/cta4j/alert/detailedalert/model/SeverityTest.java @@ -0,0 +1,33 @@ +package com.cta4j.alert.detailedalert.model; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.*; + +class SeverityTest { + @Test + void constructor_succeeds_whenScoreIsValid() { + assertThatNoException().isThrownBy(() -> new Severity(0, "000000", "normal")); + assertThatNoException().isThrownBy(() -> new Severity(99, "000000", "major")); + } + + @Test + void constructor_throwsIllegalArgumentException_whenScoreTooLow() { + assertThatIllegalArgumentException().isThrownBy(() -> new Severity(-1, "000000", "normal")); + } + + @Test + void constructor_throwsIllegalArgumentException_whenScoreTooHigh() { + assertThatIllegalArgumentException().isThrownBy(() -> new Severity(100, "000000", "normal")); + } + + @Test + void constructor_throwsNullPointerException_whenColorIsNull() { + assertThatNullPointerException().isThrownBy(() -> new Severity(50, null, "normal")); + } + + @Test + void constructor_throwsNullPointerException_whenCssIsNull() { + assertThatNullPointerException().isThrownBy(() -> new Severity(50, "000000", null)); + } +} \ No newline at end of file diff --git a/src/test/java/com/cta4j/alert/detailedalert/query/AlertsQueryTest.java b/src/test/java/com/cta4j/alert/detailedalert/query/AlertsQueryTest.java new file mode 100644 index 00000000..13966ce5 --- /dev/null +++ b/src/test/java/com/cta4j/alert/detailedalert/query/AlertsQueryTest.java @@ -0,0 +1,73 @@ +package com.cta4j.alert.detailedalert.query; + +import org.junit.jupiter.api.Test; + +import java.time.LocalDate; + +import static org.assertj.core.api.Assertions.*; + +class AlertsQueryTest { + @Test + void builder_hasCtaDefaults() { + AlertsQuery query = AlertsQuery.builder().build(); + + assertThat(query.activeOnly()).isFalse(); + assertThat(query.accessibility()).isTrue(); + assertThat(query.planned()).isTrue(); + assertThat(query.byStartDate()).isNull(); + assertThat(query.recentDays()).isNull(); + } + + @Test + void builder_buildsQueryWithOptionalParams() { + LocalDate date = LocalDate.of(2026, 7, 1); + + AlertsQuery query = AlertsQuery.builder() + .activeOnly(true) + .accessibility(false) + .planned(false) + .byStartDate(date) + .build(); + + assertThat(query.activeOnly()).isTrue(); + assertThat(query.accessibility()).isFalse(); + assertThat(query.planned()).isFalse(); + assertThat(query.byStartDate()).isEqualTo(date); + assertThat(query.recentDays()).isNull(); + } + + @Test + void builder_buildsQueryWithRecentDays() { + AlertsQuery query = AlertsQuery.builder().recentDays(7).build(); + + assertThat(query.recentDays()).isEqualTo(7); + assertThat(query.byStartDate()).isNull(); + } + + @Test + void builder_recentDays_throwsIllegalArgumentException_whenZero() { + assertThatIllegalArgumentException().isThrownBy(() -> AlertsQuery.builder().recentDays(0)); + } + + @Test + void builder_recentDays_throwsIllegalArgumentException_whenNegative() { + assertThatIllegalArgumentException().isThrownBy(() -> AlertsQuery.builder().recentDays(-1)); + } + + @Test + void builder_byStartDate_throwsNullPointerException_whenNull() { + assertThatNullPointerException().isThrownBy(() -> AlertsQuery.builder().byStartDate(null)); + } + + @Test + void constructor_throwsIllegalArgumentException_whenByStartDateAndRecentDaysBothSpecified() { + assertThatIllegalArgumentException().isThrownBy(() -> + new AlertsQuery(false, true, true, LocalDate.of(2026, 7, 1), 7)); + } + + @Test + void constructor_throwsIllegalArgumentException_whenRecentDaysIsNotPositive() { + assertThatIllegalArgumentException().isThrownBy(() -> + new AlertsQuery(false, true, true, null, 0)); + } +} \ No newline at end of file diff --git a/src/test/java/com/cta4j/alert/detailedalert/query/BusRouteAlertsQueryTest.java b/src/test/java/com/cta4j/alert/detailedalert/query/BusRouteAlertsQueryTest.java new file mode 100644 index 00000000..12de7049 --- /dev/null +++ b/src/test/java/com/cta4j/alert/detailedalert/query/BusRouteAlertsQueryTest.java @@ -0,0 +1,79 @@ +package com.cta4j.alert.detailedalert.query; + +import org.junit.jupiter.api.Test; + +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.*; + +class BusRouteAlertsQueryTest { + @Test + void builder_copiesRouteIds_andHasCtaDefaults() { + List routeIds = new ArrayList<>(List.of("22", "53")); + + BusRouteAlertsQuery query = BusRouteAlertsQuery.builder(routeIds).build(); + routeIds.add("9"); + + assertThat(query.routeIds()).containsExactly("22", "53"); + assertThat(query.activeOnly()).isFalse(); + assertThat(query.accessibility()).isTrue(); + assertThat(query.planned()).isTrue(); + } + + @Test + void builder_buildsQueryWithOptionalParams() { + BusRouteAlertsQuery query = BusRouteAlertsQuery.builder(List.of("22")) + .activeOnly(true) + .accessibility(false) + .planned(false) + .recentDays(3) + .build(); + + assertThat(query.activeOnly()).isTrue(); + assertThat(query.accessibility()).isFalse(); + assertThat(query.planned()).isFalse(); + assertThat(query.recentDays()).isEqualTo(3); + } + + @Test + void builder_setsByStartDate() { + LocalDate date = LocalDate.of(2026, 7, 1); + + BusRouteAlertsQuery query = BusRouteAlertsQuery.builder(List.of("22")) + .byStartDate(date) + .build(); + + assertThat(query.byStartDate()).isEqualTo(date); + assertThat(query.recentDays()).isNull(); + } + + @Test + void builder_throwsNullPointerException_whenRouteIdsIsNull() { + assertThatNullPointerException().isThrownBy(() -> BusRouteAlertsQuery.builder(null)); + } + + @Test + void builder_recentDays_throwsIllegalArgumentException_whenNotPositive() { + assertThatIllegalArgumentException().isThrownBy(() -> BusRouteAlertsQuery.builder(List.of("22")).recentDays(0)); + } + + @Test + void constructor_throwsNullPointerException_whenRouteIdsIsNull() { + assertThatNullPointerException().isThrownBy(() -> + new BusRouteAlertsQuery(null, false, true, true, null, null)); + } + + @Test + void constructor_throwsIllegalArgumentException_whenByStartDateAndRecentDaysBothSpecified() { + assertThatIllegalArgumentException().isThrownBy(() -> + new BusRouteAlertsQuery(List.of("22"), false, true, true, LocalDate.of(2026, 7, 1), 7)); + } + + @Test + void constructor_throwsIllegalArgumentException_whenRecentDaysIsNotPositive() { + assertThatIllegalArgumentException().isThrownBy(() -> + new BusRouteAlertsQuery(List.of("22"), false, true, true, null, -1)); + } +} \ No newline at end of file diff --git a/src/test/java/com/cta4j/alert/detailedalert/query/LineAlertsQueryTest.java b/src/test/java/com/cta4j/alert/detailedalert/query/LineAlertsQueryTest.java new file mode 100644 index 00000000..9459d5be --- /dev/null +++ b/src/test/java/com/cta4j/alert/detailedalert/query/LineAlertsQueryTest.java @@ -0,0 +1,81 @@ +package com.cta4j.alert.detailedalert.query; + +import com.cta4j.alert.common.model.AlertTrainLine; +import org.junit.jupiter.api.Test; + +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.*; + +class LineAlertsQueryTest { + @Test + void builder_copiesLines_andHasCtaDefaults() { + List lines = new ArrayList<>(List.of(AlertTrainLine.RED, AlertTrainLine.BLUE)); + + LineAlertsQuery query = LineAlertsQuery.builder(lines).build(); + lines.add(AlertTrainLine.GREEN); + + assertThat(query.lines()).containsExactly(AlertTrainLine.RED, AlertTrainLine.BLUE); + assertThat(query.activeOnly()).isFalse(); + assertThat(query.accessibility()).isTrue(); + assertThat(query.planned()).isTrue(); + } + + @Test + void builder_buildsQueryWithOptionalParams() { + LineAlertsQuery query = LineAlertsQuery.builder(List.of(AlertTrainLine.RED)) + .activeOnly(true) + .accessibility(false) + .planned(false) + .recentDays(3) + .build(); + + assertThat(query.activeOnly()).isTrue(); + assertThat(query.accessibility()).isFalse(); + assertThat(query.planned()).isFalse(); + assertThat(query.recentDays()).isEqualTo(3); + } + + @Test + void builder_setsByStartDate() { + LocalDate date = LocalDate.of(2026, 7, 1); + + LineAlertsQuery query = LineAlertsQuery.builder(List.of(AlertTrainLine.RED)) + .byStartDate(date) + .build(); + + assertThat(query.byStartDate()).isEqualTo(date); + assertThat(query.recentDays()).isNull(); + } + + @Test + void builder_recentDays_throwsIllegalArgumentException_whenNotPositive() { + assertThatIllegalArgumentException().isThrownBy(() -> + LineAlertsQuery.builder(List.of(AlertTrainLine.RED)).recentDays(0)); + } + + @Test + void builder_throwsNullPointerException_whenLinesIsNull() { + assertThatNullPointerException().isThrownBy(() -> LineAlertsQuery.builder(null)); + } + + @Test + void constructor_throwsNullPointerException_whenLinesIsNull() { + assertThatNullPointerException().isThrownBy(() -> + new LineAlertsQuery(null, false, true, true, null, null)); + } + + @Test + void constructor_throwsIllegalArgumentException_whenByStartDateAndRecentDaysBothSpecified() { + assertThatIllegalArgumentException().isThrownBy(() -> + new LineAlertsQuery(List.of(AlertTrainLine.RED), false, true, true, LocalDate.of(2026, 7, 1), 7)); + } + + @Test + void constructor_throwsIllegalArgumentException_whenRecentDaysIsNotPositive() { + assertThatIllegalArgumentException().isThrownBy(() -> + new LineAlertsQuery(List.of(AlertTrainLine.RED), false, true, true, null, -1)); + } +} \ No newline at end of file diff --git a/src/test/java/com/cta4j/alert/detailedalert/query/StationAlertsQueryTest.java b/src/test/java/com/cta4j/alert/detailedalert/query/StationAlertsQueryTest.java new file mode 100644 index 00000000..1395f5a1 --- /dev/null +++ b/src/test/java/com/cta4j/alert/detailedalert/query/StationAlertsQueryTest.java @@ -0,0 +1,78 @@ +package com.cta4j.alert.detailedalert.query; + +import org.junit.jupiter.api.Test; + +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.*; + +class StationAlertsQueryTest { + @Test + void builder_copiesStationIds_andHasCtaDefaults() { + List stationIds = new ArrayList<>(List.of("40380", "41260")); + + StationAlertsQuery query = StationAlertsQuery.builder(stationIds).build(); + stationIds.add("40560"); + + assertThat(query.stationIds()).containsExactly("40380", "41260"); + assertThat(query.activeOnly()).isFalse(); + assertThat(query.accessibility()).isTrue(); + assertThat(query.planned()).isTrue(); + } + + @Test + void builder_buildsQueryWithOptionalParams() { + StationAlertsQuery query = StationAlertsQuery.builder(List.of("40380")) + .activeOnly(true) + .accessibility(false) + .planned(false) + .byStartDate(LocalDate.of(2026, 7, 1)) + .build(); + + assertThat(query.activeOnly()).isTrue(); + assertThat(query.accessibility()).isFalse(); + assertThat(query.planned()).isFalse(); + assertThat(query.byStartDate()).isEqualTo(LocalDate.of(2026, 7, 1)); + } + + @Test + void builder_setsRecentDays() { + StationAlertsQuery query = StationAlertsQuery.builder(List.of("40380")) + .recentDays(7) + .build(); + + assertThat(query.recentDays()).isEqualTo(7); + assertThat(query.byStartDate()).isNull(); + } + + @Test + void builder_recentDays_throwsIllegalArgumentException_whenNotPositive() { + assertThatIllegalArgumentException().isThrownBy(() -> + StationAlertsQuery.builder(List.of("40380")).recentDays(0)); + } + + @Test + void builder_throwsNullPointerException_whenStationIdsIsNull() { + assertThatNullPointerException().isThrownBy(() -> StationAlertsQuery.builder(null)); + } + + @Test + void constructor_throwsNullPointerException_whenStationIdsIsNull() { + assertThatNullPointerException().isThrownBy(() -> + new StationAlertsQuery(null, false, true, true, null, null)); + } + + @Test + void constructor_throwsIllegalArgumentException_whenByStartDateAndRecentDaysBothSpecified() { + assertThatIllegalArgumentException().isThrownBy(() -> + new StationAlertsQuery(List.of("40380"), false, true, true, LocalDate.of(2026, 7, 1), 7)); + } + + @Test + void constructor_throwsIllegalArgumentException_whenRecentDaysIsNotPositive() { + assertThatIllegalArgumentException().isThrownBy(() -> + new StationAlertsQuery(List.of("40380"), false, true, true, null, -1)); + } +} \ No newline at end of file diff --git a/src/test/java/com/cta4j/common/internal/util/BooleanParserTest.java b/src/test/java/com/cta4j/common/internal/util/BooleanParserTest.java new file mode 100644 index 00000000..89fd6536 --- /dev/null +++ b/src/test/java/com/cta4j/common/internal/util/BooleanParserTest.java @@ -0,0 +1,38 @@ +package com.cta4j.common.internal.util; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.*; + +class BooleanParserTest { + @Test + void parse01_returnsFalse_whenValueIsZero() { + assertThat(BooleanParser.parse01("0")).isFalse(); + } + + @Test + void parse01_returnsTrue_whenValueIsOne() { + assertThat(BooleanParser.parse01("1")).isTrue(); + } + + @Test + void parse01_throwsIllegalArgumentException_whenValueIsInvalid() { + assertThatIllegalArgumentException().isThrownBy(() -> BooleanParser.parse01("2")) + .withMessage("Invalid value: 2. Expected 0 or 1"); + } + + @Test + void parse01_throwsIllegalArgumentException_whenValueIsBlank() { + assertThatIllegalArgumentException().isThrownBy(() -> BooleanParser.parse01("")); + } + + @Test + void parse01_throwsIllegalArgumentException_whenValueIsTrueOrFalseString() { + assertThatIllegalArgumentException().isThrownBy(() -> BooleanParser.parse01("true")); + } + + @Test + void parse01_throwsNullPointerException_whenValueIsNull() { + assertThatNullPointerException().isThrownBy(() -> BooleanParser.parse01(null)); + } +} \ No newline at end of file diff --git a/src/test/java/com/cta4j/common/internal/util/TimestampParserTest.java b/src/test/java/com/cta4j/common/internal/util/TimestampParserTest.java new file mode 100644 index 00000000..755b9e74 --- /dev/null +++ b/src/test/java/com/cta4j/common/internal/util/TimestampParserTest.java @@ -0,0 +1,73 @@ +package com.cta4j.common.internal.util; + +import org.junit.jupiter.api.Test; + +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; + +import static org.assertj.core.api.Assertions.*; + +class TimestampParserTest { + private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"); + + @Test + void parse_returnsInstant_atProvidedZone() { + Instant result = TimestampParser.parse("2026-07-28T12:00:00", FORMATTER, ZoneOffset.UTC); + + assertThat(result).isEqualTo(Instant.parse("2026-07-28T12:00:00Z")); + } + + @Test + void parse_appliesZoneId_whenConvertingToInstant() { + Instant utc = TimestampParser.parse("2026-07-28T12:00:00", FORMATTER, ZoneOffset.UTC); + Instant chicago = TimestampParser.parse("2026-07-28T12:00:00", FORMATTER, ZoneId.of("America/Chicago")); + + assertThat(chicago).isAfter(utc); + } + + @Test + void parse_throwsIllegalArgumentException_whenTimestampDoesNotMatchFormatter() { + assertThatIllegalArgumentException() + .isThrownBy(() -> TimestampParser.parse("not-a-timestamp", FORMATTER, ZoneOffset.UTC)) + .withMessageContaining("Failed to parse timestamp: not-a-timestamp") + .withCauseInstanceOf(DateTimeParseException.class); + } + + @Test + void parse_throwsNullPointerException_whenTimestampIsNull() { + assertThatNullPointerException().isThrownBy(() -> TimestampParser.parse(null, FORMATTER, ZoneOffset.UTC)); + } + + @Test + void parse_throwsNullPointerException_whenFormatterIsNull() { + assertThatNullPointerException() + .isThrownBy(() -> TimestampParser.parse("2026-07-28T12:00:00", null, ZoneOffset.UTC)); + } + + @Test + void parse_throwsNullPointerException_whenZoneIdIsNull() { + assertThatNullPointerException() + .isThrownBy(() -> TimestampParser.parse("2026-07-28T12:00:00", FORMATTER, null)); + } + + @Test + void parseNullable_returnsNull_whenTimestampIsNull() { + assertThat(TimestampParser.parseNullable(null, FORMATTER, ZoneOffset.UTC)).isNull(); + } + + @Test + void parseNullable_returnsInstant_whenTimestampIsNonNull() { + Instant result = TimestampParser.parseNullable("2026-07-28T12:00:00", FORMATTER, ZoneOffset.UTC); + + assertThat(result).isEqualTo(Instant.parse("2026-07-28T12:00:00Z")); + } + + @Test + void parseNullable_throwsIllegalArgumentException_whenTimestampDoesNotMatchFormatter() { + assertThatIllegalArgumentException() + .isThrownBy(() -> TimestampParser.parseNullable("bad", FORMATTER, ZoneOffset.UTC)); + } +} \ No newline at end of file diff --git a/src/test/resources/alert/detailedalert/bad_error_code.json b/src/test/resources/alert/detailedalert/bad_error_code.json new file mode 100644 index 00000000..a682cce1 --- /dev/null +++ b/src/test/resources/alert/detailedalert/bad_error_code.json @@ -0,0 +1,7 @@ +{ + "CTAAlerts": { + "TimeStamp": "2026-07-28T12:00:00", + "ErrorCode": "abc", + "ErrorMessage": "Something odd happened" + } +} \ No newline at end of file diff --git a/src/test/resources/alert/detailedalert/empty_alert_array.json b/src/test/resources/alert/detailedalert/empty_alert_array.json new file mode 100644 index 00000000..10689d04 --- /dev/null +++ b/src/test/resources/alert/detailedalert/empty_alert_array.json @@ -0,0 +1,8 @@ +{ + "CTAAlerts": { + "TimeStamp": "2026-07-28T12:00:00", + "ErrorCode": "0", + "ErrorMessage": null, + "Alert": [] + } +} \ No newline at end of file diff --git a/src/test/resources/alert/detailedalert/error_message_absent.json b/src/test/resources/alert/detailedalert/error_message_absent.json new file mode 100644 index 00000000..9ecb9b88 --- /dev/null +++ b/src/test/resources/alert/detailedalert/error_message_absent.json @@ -0,0 +1,6 @@ +{ + "CTAAlerts": { + "TimeStamp": "2026-07-28T12:00:00", + "ErrorCode": "500" + } +} \ No newline at end of file diff --git a/src/test/resources/alert/detailedalert/error_message_blank.json b/src/test/resources/alert/detailedalert/error_message_blank.json new file mode 100644 index 00000000..bf2f6944 --- /dev/null +++ b/src/test/resources/alert/detailedalert/error_message_blank.json @@ -0,0 +1,7 @@ +{ + "CTAAlerts": { + "TimeStamp": "2026-07-28T12:00:00", + "ErrorCode": "900", + "ErrorMessage": "" + } +} \ No newline at end of file diff --git a/src/test/resources/alert/detailedalert/fatal_error.json b/src/test/resources/alert/detailedalert/fatal_error.json new file mode 100644 index 00000000..96be25a9 --- /dev/null +++ b/src/test/resources/alert/detailedalert/fatal_error.json @@ -0,0 +1,7 @@ +{ + "CTAAlerts": { + "TimeStamp": "2026-07-28T12:00:00", + "ErrorCode": "100", + "ErrorMessage": "Invalid option for parameter 'activeonly': Valid options are 'true', 'false'" + } +} \ No newline at end of file diff --git a/src/test/resources/alert/detailedalert/list_success.json b/src/test/resources/alert/detailedalert/list_success.json new file mode 100644 index 00000000..88f89f43 --- /dev/null +++ b/src/test/resources/alert/detailedalert/list_success.json @@ -0,0 +1,104 @@ +{ + "CTAAlerts": { + "TimeStamp": "2026-07-28T12:00:00", + "ErrorCode": "0", + "ErrorMessage": null, + "Alert": [ + { + "AlertId": "115070", + "Headline": "Route 22 Rerouted", + "ShortDescription": "Route 22 is rerouted due to construction", + "FullDescription": {"#cdata-section": "Route 22 buses are being rerouted due to construction on Clark St."}, + "SeverityScore": "37", + "SeverityColor": "06c", + "SeverityCSS": "planned", + "Impact": "Planned Reroute", + "EventStart": "2026-07-01T05:00:00", + "EventEnd": "2026-08-01T05:00:00", + "TBD": "0", + "MajorAlert": "0", + "AlertURL": {"#cdata-section": "http://www.transitchicago.com/alerts/115070"}, + "ImpactedService": { + "Service": { + "ServiceType": "B", + "ServiceTypeDescription": "Bus Route", + "ServiceName": "Clark", + "ServiceId": "22", + "ServiceBackColor": "565a5c", + "ServiceTextColor": "ffffff", + "ServiceURL": {"#cdata-section": "http://www.transitchicago.com/bus/22/"} + } + }, + "ttim": "0", + "GUID": "664b81c1-197b-450b-a00c-090483b90bb9" + }, + { + "AlertId": "115080", + "Headline": "Austin Main Stationhouse Temporarily Closed", + "ShortDescription": "Elevator out of service", + "FullDescription": {"#cdata-section": "The elevator at Austin is out of service."}, + "SeverityScore": "9", + "SeverityColor": "000000", + "SeverityCSS": "minor", + "Impact": "Elevator Status", + "EventStart": "2026-07-15T00:00:00", + "EventEnd": null, + "TBD": "1", + "MajorAlert": "0", + "AlertURL": {"#cdata-section": "http://www.transitchicago.com/alerts/115080"}, + "ImpactedService": { + "Service": [ + { + "ServiceType": "T", + "ServiceTypeDescription": "Train Station", + "ServiceName": "Austin", + "ServiceId": "41260", + "ServiceBackColor": "009b3a", + "ServiceTextColor": "FFFFFF", + "ServiceURL": {"#cdata-section": "http://www.transitchicago.com/travel_information/station.aspx?StopId=24"} + }, + { + "ServiceType": "R", + "ServiceTypeDescription": "Train Route", + "ServiceName": "Green Line", + "ServiceId": "G", + "ServiceBackColor": "009b3a", + "ServiceTextColor": "FFFFFF", + "ServiceURL": {"#cdata-section": "http://www.transitchicago.com/greenline/"} + } + ] + }, + "ttim": "1", + "GUID": "d41b2532-09ca-4827-b9c6-f4299cc86fb6" + }, + { + "AlertId": "115090", + "Headline": "Later, More Frequent Weekend Service", + "ShortDescription": "Service is being increased on the South Pulaski corridor.", + "FullDescription": {"#cdata-section": "Later evening and more frequent weekend service will operate on the South Pulaski corridor."}, + "SeverityScore": "11", + "SeverityColor": "000000", + "SeverityCSS": "normal", + "Impact": "Added Service", + "EventStart": "2025-11-07", + "EventEnd": "2027-09-30", + "TBD": "0", + "MajorAlert": "0", + "AlertURL": {"#cdata-section": "http://www.transitchicago.com/alerts/115090"}, + "ImpactedService": { + "Service": { + "ServiceType": "B", + "ServiceTypeDescription": "Bus Route", + "ServiceName": "South Pulaski", + "ServiceId": "53A", + "ServiceBackColor": "059", + "ServiceTextColor": "ffffff", + "ServiceURL": {"#cdata-section": "http://www.transitchicago.com/riding_cta/bus_route.aspx?RouteId=207"} + } + }, + "ttim": "0", + "GUID": "9979cd0c-a29d-4b52-805d-4baa0b32322b" + } + ] + } +} \ No newline at end of file diff --git a/src/test/resources/alert/detailedalert/no_active_alerts.json b/src/test/resources/alert/detailedalert/no_active_alerts.json new file mode 100644 index 00000000..960a3f70 --- /dev/null +++ b/src/test/resources/alert/detailedalert/no_active_alerts.json @@ -0,0 +1,7 @@ +{ + "CTAAlerts": { + "TimeStamp": "2026-07-28T12:00:00", + "ErrorCode": "25", + "ErrorMessage": "There are no active alerts" + } +} \ No newline at end of file diff --git a/src/test/resources/alert/detailedalert/no_active_alerts_for_filter.json b/src/test/resources/alert/detailedalert/no_active_alerts_for_filter.json new file mode 100644 index 00000000..ae285751 --- /dev/null +++ b/src/test/resources/alert/detailedalert/no_active_alerts_for_filter.json @@ -0,0 +1,7 @@ +{ + "CTAAlerts": { + "TimeStamp": "2026-07-28T12:00:00", + "ErrorCode": "50", + "ErrorMessage": "There are no active alerts based on your filter criteria" + } +} \ No newline at end of file diff --git a/src/test/resources/alert/detailedalert/ok_no_alerts.json b/src/test/resources/alert/detailedalert/ok_no_alerts.json new file mode 100644 index 00000000..1b01c6fa --- /dev/null +++ b/src/test/resources/alert/detailedalert/ok_no_alerts.json @@ -0,0 +1,6 @@ +{ + "CTAAlerts": { + "TimeStamp": "2026-07-28T12:00:00", + "ErrorCode": "0" + } +} \ No newline at end of file From 4501db91a7cc7014bc572bb51c6c45c2da542a6c Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Tue, 28 Jul 2026 16:18:10 -0500 Subject: [PATCH 35/60] Fix test files to ensure proper newline at end of file; enhance readability and maintain coding standards --- .../java/com/cta4j/alert/common/model/AlertTrainLineTest.java | 2 +- .../java/com/cta4j/alert/detailedalert/AlertMapperTest.java | 2 +- .../cta4j/alert/detailedalert/DetailedAlertsApiImplTest.java | 2 +- .../exception/Cta4jDetailedAlertsExceptionTest.java | 2 +- .../detailedalert/exception/DetailedAlertsErrorCodeTest.java | 2 +- .../cta4j/alert/detailedalert/internal/wire/CtaAlertsTest.java | 2 +- .../detailedalert/internal/wire/CtaImpactedServicesTest.java | 2 +- .../java/com/cta4j/alert/detailedalert/model/SeverityTest.java | 2 +- .../com/cta4j/alert/detailedalert/query/AlertsQueryTest.java | 2 +- .../alert/detailedalert/query/BusRouteAlertsQueryTest.java | 2 +- .../cta4j/alert/detailedalert/query/LineAlertsQueryTest.java | 2 +- .../cta4j/alert/detailedalert/query/StationAlertsQueryTest.java | 2 +- .../java/com/cta4j/common/internal/util/BooleanParserTest.java | 2 +- .../com/cta4j/common/internal/util/TimestampParserTest.java | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/test/java/com/cta4j/alert/common/model/AlertTrainLineTest.java b/src/test/java/com/cta4j/alert/common/model/AlertTrainLineTest.java index 3743f117..cea3e5d6 100644 --- a/src/test/java/com/cta4j/alert/common/model/AlertTrainLineTest.java +++ b/src/test/java/com/cta4j/alert/common/model/AlertTrainLineTest.java @@ -48,4 +48,4 @@ void getCode_returnsCode() { assertThat(AlertTrainLine.PINK.getCode()).isEqualTo("Pink"); assertThat(AlertTrainLine.YELLOW.getCode()).isEqualTo("Y"); } -} \ No newline at end of file +} diff --git a/src/test/java/com/cta4j/alert/detailedalert/AlertMapperTest.java b/src/test/java/com/cta4j/alert/detailedalert/AlertMapperTest.java index 8150be0d..c68f0082 100644 --- a/src/test/java/com/cta4j/alert/detailedalert/AlertMapperTest.java +++ b/src/test/java/com/cta4j/alert/detailedalert/AlertMapperTest.java @@ -178,4 +178,4 @@ void toDomain_mapsMultipleImpactedServices() { assertThat(alert.impactedServices().get(0).type()).isEqualTo(ServiceType.STATION); assertThat(alert.impactedServices().get(1).type()).isEqualTo(ServiceType.RAIL); } -} \ No newline at end of file +} diff --git a/src/test/java/com/cta4j/alert/detailedalert/DetailedAlertsApiImplTest.java b/src/test/java/com/cta4j/alert/detailedalert/DetailedAlertsApiImplTest.java index 32cce1d0..3926a24a 100644 --- a/src/test/java/com/cta4j/alert/detailedalert/DetailedAlertsApiImplTest.java +++ b/src/test/java/com/cta4j/alert/detailedalert/DetailedAlertsApiImplTest.java @@ -435,4 +435,4 @@ void findByStationId_delegatesToFindByStationIds() { assertThat(alerts).hasSize(3); } -} \ No newline at end of file +} diff --git a/src/test/java/com/cta4j/alert/detailedalert/exception/Cta4jDetailedAlertsExceptionTest.java b/src/test/java/com/cta4j/alert/detailedalert/exception/Cta4jDetailedAlertsExceptionTest.java index ad959a52..bce1a8df 100644 --- a/src/test/java/com/cta4j/alert/detailedalert/exception/Cta4jDetailedAlertsExceptionTest.java +++ b/src/test/java/com/cta4j/alert/detailedalert/exception/Cta4jDetailedAlertsExceptionTest.java @@ -40,4 +40,4 @@ void constructor_setsUnknownErrorCode_whenRawErrorCodeIsUnrecognized() { assertThat(exception.getRawErrorCode()).isEqualTo(999); assertThat(exception.getErrorCode()).isEqualTo(DetailedAlertsErrorCode.UNKNOWN); } -} \ No newline at end of file +} diff --git a/src/test/java/com/cta4j/alert/detailedalert/exception/DetailedAlertsErrorCodeTest.java b/src/test/java/com/cta4j/alert/detailedalert/exception/DetailedAlertsErrorCodeTest.java index 8c825016..02570a46 100644 --- a/src/test/java/com/cta4j/alert/detailedalert/exception/DetailedAlertsErrorCodeTest.java +++ b/src/test/java/com/cta4j/alert/detailedalert/exception/DetailedAlertsErrorCodeTest.java @@ -23,4 +23,4 @@ void getCode_returnsCode() { assertThat(DetailedAlertsErrorCode.NO_ACTIVE_ALERTS_FOR_FILTER.getCode()).isEqualTo(50); assertThat(DetailedAlertsErrorCode.UNKNOWN.getCode()).isEqualTo(-1); } -} \ No newline at end of file +} diff --git a/src/test/java/com/cta4j/alert/detailedalert/internal/wire/CtaAlertsTest.java b/src/test/java/com/cta4j/alert/detailedalert/internal/wire/CtaAlertsTest.java index 75adaf74..7f060ca6 100644 --- a/src/test/java/com/cta4j/alert/detailedalert/internal/wire/CtaAlertsTest.java +++ b/src/test/java/com/cta4j/alert/detailedalert/internal/wire/CtaAlertsTest.java @@ -56,4 +56,4 @@ private static CtaAlert newAlert(String alertId) { null ); } -} \ No newline at end of file +} diff --git a/src/test/java/com/cta4j/alert/detailedalert/internal/wire/CtaImpactedServicesTest.java b/src/test/java/com/cta4j/alert/detailedalert/internal/wire/CtaImpactedServicesTest.java index b998c88f..714c1e0c 100644 --- a/src/test/java/com/cta4j/alert/detailedalert/internal/wire/CtaImpactedServicesTest.java +++ b/src/test/java/com/cta4j/alert/detailedalert/internal/wire/CtaImpactedServicesTest.java @@ -27,4 +27,4 @@ void constructor_copiesService() { void constructor_throwsNullPointerException_whenServiceIsNull() { assertThatNullPointerException().isThrownBy(() -> new CtaImpactedServices(null)); } -} \ No newline at end of file +} diff --git a/src/test/java/com/cta4j/alert/detailedalert/model/SeverityTest.java b/src/test/java/com/cta4j/alert/detailedalert/model/SeverityTest.java index 3af73013..99f48ac6 100644 --- a/src/test/java/com/cta4j/alert/detailedalert/model/SeverityTest.java +++ b/src/test/java/com/cta4j/alert/detailedalert/model/SeverityTest.java @@ -30,4 +30,4 @@ void constructor_throwsNullPointerException_whenColorIsNull() { void constructor_throwsNullPointerException_whenCssIsNull() { assertThatNullPointerException().isThrownBy(() -> new Severity(50, "000000", null)); } -} \ No newline at end of file +} diff --git a/src/test/java/com/cta4j/alert/detailedalert/query/AlertsQueryTest.java b/src/test/java/com/cta4j/alert/detailedalert/query/AlertsQueryTest.java index 13966ce5..cdfeca7e 100644 --- a/src/test/java/com/cta4j/alert/detailedalert/query/AlertsQueryTest.java +++ b/src/test/java/com/cta4j/alert/detailedalert/query/AlertsQueryTest.java @@ -70,4 +70,4 @@ void constructor_throwsIllegalArgumentException_whenRecentDaysIsNotPositive() { assertThatIllegalArgumentException().isThrownBy(() -> new AlertsQuery(false, true, true, null, 0)); } -} \ No newline at end of file +} diff --git a/src/test/java/com/cta4j/alert/detailedalert/query/BusRouteAlertsQueryTest.java b/src/test/java/com/cta4j/alert/detailedalert/query/BusRouteAlertsQueryTest.java index 12de7049..13765f9d 100644 --- a/src/test/java/com/cta4j/alert/detailedalert/query/BusRouteAlertsQueryTest.java +++ b/src/test/java/com/cta4j/alert/detailedalert/query/BusRouteAlertsQueryTest.java @@ -76,4 +76,4 @@ void constructor_throwsIllegalArgumentException_whenRecentDaysIsNotPositive() { assertThatIllegalArgumentException().isThrownBy(() -> new BusRouteAlertsQuery(List.of("22"), false, true, true, null, -1)); } -} \ No newline at end of file +} diff --git a/src/test/java/com/cta4j/alert/detailedalert/query/LineAlertsQueryTest.java b/src/test/java/com/cta4j/alert/detailedalert/query/LineAlertsQueryTest.java index 9459d5be..920d3c97 100644 --- a/src/test/java/com/cta4j/alert/detailedalert/query/LineAlertsQueryTest.java +++ b/src/test/java/com/cta4j/alert/detailedalert/query/LineAlertsQueryTest.java @@ -78,4 +78,4 @@ void constructor_throwsIllegalArgumentException_whenRecentDaysIsNotPositive() { assertThatIllegalArgumentException().isThrownBy(() -> new LineAlertsQuery(List.of(AlertTrainLine.RED), false, true, true, null, -1)); } -} \ No newline at end of file +} diff --git a/src/test/java/com/cta4j/alert/detailedalert/query/StationAlertsQueryTest.java b/src/test/java/com/cta4j/alert/detailedalert/query/StationAlertsQueryTest.java index 1395f5a1..5519d0ef 100644 --- a/src/test/java/com/cta4j/alert/detailedalert/query/StationAlertsQueryTest.java +++ b/src/test/java/com/cta4j/alert/detailedalert/query/StationAlertsQueryTest.java @@ -75,4 +75,4 @@ void constructor_throwsIllegalArgumentException_whenRecentDaysIsNotPositive() { assertThatIllegalArgumentException().isThrownBy(() -> new StationAlertsQuery(List.of("40380"), false, true, true, null, -1)); } -} \ No newline at end of file +} diff --git a/src/test/java/com/cta4j/common/internal/util/BooleanParserTest.java b/src/test/java/com/cta4j/common/internal/util/BooleanParserTest.java index 89fd6536..61ea9c53 100644 --- a/src/test/java/com/cta4j/common/internal/util/BooleanParserTest.java +++ b/src/test/java/com/cta4j/common/internal/util/BooleanParserTest.java @@ -35,4 +35,4 @@ void parse01_throwsIllegalArgumentException_whenValueIsTrueOrFalseString() { void parse01_throwsNullPointerException_whenValueIsNull() { assertThatNullPointerException().isThrownBy(() -> BooleanParser.parse01(null)); } -} \ No newline at end of file +} diff --git a/src/test/java/com/cta4j/common/internal/util/TimestampParserTest.java b/src/test/java/com/cta4j/common/internal/util/TimestampParserTest.java index 755b9e74..3f206056 100644 --- a/src/test/java/com/cta4j/common/internal/util/TimestampParserTest.java +++ b/src/test/java/com/cta4j/common/internal/util/TimestampParserTest.java @@ -70,4 +70,4 @@ void parseNullable_throwsIllegalArgumentException_whenTimestampDoesNotMatchForma assertThatIllegalArgumentException() .isThrownBy(() -> TimestampParser.parseNullable("bad", FORMATTER, ZoneOffset.UTC)); } -} \ No newline at end of file +} From 4647414268137add33ef18d9c4e91779ccc6052a Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Tue, 28 Jul 2026 17:56:26 -0500 Subject: [PATCH 36/60] added testing cleanup --- .../train/common/model/TrainDirection.java | 5 --- .../cta4j/bus/detour/DetoursApiImplTest.java | 13 ++++++ .../bus/direction/DirectionsApiImplTest.java | 13 ++++++ .../cta4j/bus/locale/LocalesApiImplTest.java | 13 ++++++ .../bus/pattern/PatternsApiImplTest.java | 13 ++++++ .../prediction/PredictionsApiImplTest.java | 42 +++++++++++++++++++ .../cta4j/bus/route/RoutesApiImplTest.java | 13 ++++++ .../com/cta4j/bus/stop/StopMapperTest.java | 29 +++++++++++++ .../bus/vehicle/VehiclesApiImplTest.java | 13 ++++++ .../train/arrival/ArrivalsApiImplTest.java | 18 ++++++++ .../cta4j/train/follow/FollowApiImplTest.java | 16 +++++++ .../train/location/LocationsApiImplTest.java | 16 +++++++ .../bus/detour/empty_dtrs_array.json | 5 +++ .../bus/direction/empty_directions_array.json | 5 +++ .../bus/locale/empty_locale_array.json | 5 +++ .../bus/pattern/empty_ptr_array.json | 5 +++ .../bus/prediction/empty_prd_array.json | 5 +++ .../bus/route/empty_routes_array.json | 5 +++ .../bus/vehicle/empty_vehicle_array.json | 5 +++ 19 files changed, 234 insertions(+), 5 deletions(-) create mode 100644 src/test/resources/bus/detour/empty_dtrs_array.json create mode 100644 src/test/resources/bus/direction/empty_directions_array.json create mode 100644 src/test/resources/bus/locale/empty_locale_array.json create mode 100644 src/test/resources/bus/pattern/empty_ptr_array.json create mode 100644 src/test/resources/bus/prediction/empty_prd_array.json create mode 100644 src/test/resources/bus/route/empty_routes_array.json create mode 100644 src/test/resources/bus/vehicle/empty_vehicle_array.json diff --git a/src/main/java/com/cta4j/train/common/model/TrainDirection.java b/src/main/java/com/cta4j/train/common/model/TrainDirection.java index 270fb636..f299505e 100644 --- a/src/main/java/com/cta4j/train/common/model/TrainDirection.java +++ b/src/main/java/com/cta4j/train/common/model/TrainDirection.java @@ -31,13 +31,8 @@ public enum TrainDirection { * Constructs a {@code TrainDirection}. * * @param code the CTA direction code associated with this train direction - * @throws IllegalArgumentException if {@code code} is not 1 (northbound) or 5 (southbound) */ TrainDirection(int code) { - if ((code != 1) && (code != 5)) { - throw new IllegalArgumentException("CTA direction code must be either 1 (northbound) or 5 (southbound)"); - } - this.code = code; } diff --git a/src/test/java/com/cta4j/bus/detour/DetoursApiImplTest.java b/src/test/java/com/cta4j/bus/detour/DetoursApiImplTest.java index e46c498b..29b2cf95 100644 --- a/src/test/java/com/cta4j/bus/detour/DetoursApiImplTest.java +++ b/src/test/java/com/cta4j/bus/detour/DetoursApiImplTest.java @@ -66,6 +66,19 @@ void list_returnsEmpty_whenResponseHasNoDataAndNoErrors() { assertThat(detours).isEmpty(); } + @Test + void list_returnsEmpty_whenDtrsIsExplicitlyEmptyArray() { + this.server.stubFor(get(urlPathEqualTo("/bustime/api/v3/getdetours")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("bus/detour/empty_dtrs_array.json")))); + + List detours = this.api.list(); + + assertThat(detours).isEmpty(); + } + @Test void list_throwsCta4jBusException_whenResponseContainsFatalErrors() { this.server.stubFor(get(urlPathEqualTo("/bustime/api/v3/getdetours")) diff --git a/src/test/java/com/cta4j/bus/direction/DirectionsApiImplTest.java b/src/test/java/com/cta4j/bus/direction/DirectionsApiImplTest.java index de5c6057..86a754c8 100644 --- a/src/test/java/com/cta4j/bus/direction/DirectionsApiImplTest.java +++ b/src/test/java/com/cta4j/bus/direction/DirectionsApiImplTest.java @@ -60,6 +60,19 @@ void findByRouteId_returnsEmpty_whenResponseHasNoDataAndNoErrors() { assertThat(directions).isEmpty(); } + @Test + void findByRouteId_returnsEmpty_whenDirectionsIsExplicitlyEmptyArray() { + this.server.stubFor(get(urlPathEqualTo("/bustime/api/v3/getdirections")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("bus/direction/empty_directions_array.json")))); + + List directions = this.api.findByRouteId("22"); + + assertThat(directions).isEmpty(); + } + @Test void findByRouteId_returnsEmpty_whenAllErrorsAreResourceSpecific() { this.server.stubFor(get(urlPathEqualTo("/bustime/api/v3/getdirections")) diff --git a/src/test/java/com/cta4j/bus/locale/LocalesApiImplTest.java b/src/test/java/com/cta4j/bus/locale/LocalesApiImplTest.java index 9f7803c8..9a7c09f4 100644 --- a/src/test/java/com/cta4j/bus/locale/LocalesApiImplTest.java +++ b/src/test/java/com/cta4j/bus/locale/LocalesApiImplTest.java @@ -65,6 +65,19 @@ void list_returnsEmpty_whenResponseHasNoDataAndNoErrors() { assertThat(locales).isEmpty(); } + @Test + void list_returnsEmpty_whenLocaleIsExplicitlyEmptyArray() { + this.server.stubFor(get(urlPathEqualTo("/bustime/api/v3/getlocalelist")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("bus/locale/empty_locale_array.json")))); + + List locales = this.api.list(); + + assertThat(locales).isEmpty(); + } + @Test void list_throwsCta4jBusException_whenResponseContainsFatalErrors() { this.server.stubFor(get(urlPathEqualTo("/bustime/api/v3/getlocalelist")) diff --git a/src/test/java/com/cta4j/bus/pattern/PatternsApiImplTest.java b/src/test/java/com/cta4j/bus/pattern/PatternsApiImplTest.java index 15450ec4..f50ff7b8 100644 --- a/src/test/java/com/cta4j/bus/pattern/PatternsApiImplTest.java +++ b/src/test/java/com/cta4j/bus/pattern/PatternsApiImplTest.java @@ -75,6 +75,19 @@ void findByIds_returnsEmpty_whenResponseHasNoDataAndNoErrors() { assertThat(patterns).isEmpty(); } + @Test + void findByIds_returnsEmpty_whenPtrIsExplicitlyEmptyArray() { + this.server.stubFor(get(urlPathEqualTo("/bustime/api/v3/getpatterns")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("bus/pattern/empty_ptr_array.json")))); + + List patterns = this.api.findByIds(List.of("3630")); + + assertThat(patterns).isEmpty(); + } + @Test void findByIds_returnsEmpty_whenAllErrorsAreResourceSpecific() { this.server.stubFor(get(urlPathEqualTo("/bustime/api/v3/getpatterns")) diff --git a/src/test/java/com/cta4j/bus/prediction/PredictionsApiImplTest.java b/src/test/java/com/cta4j/bus/prediction/PredictionsApiImplTest.java index e668f034..8b94fc92 100644 --- a/src/test/java/com/cta4j/bus/prediction/PredictionsApiImplTest.java +++ b/src/test/java/com/cta4j/bus/prediction/PredictionsApiImplTest.java @@ -78,6 +78,20 @@ void findByStopIds_returnsEmpty_whenResponseHasNoDataAndNoErrors() { assertThat(predictions).isEmpty(); } + @Test + void findByStopIds_returnsEmpty_whenPrdIsExplicitlyEmptyArray() { + this.server.stubFor(get(urlPathEqualTo("/bustime/api/v3/getpredictions")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("bus/prediction/empty_prd_array.json")))); + + StopPredictionsQuery query = StopPredictionsQuery.builder(List.of("456")).build(); + List predictions = this.api.findByStopIds(query); + + assertThat(predictions).isEmpty(); + } + @Test void findByStopIds_returnsEmpty_whenAllErrorsAreResourceSpecific() { this.server.stubFor(get(urlPathEqualTo("/bustime/api/v3/getpredictions")) @@ -260,4 +274,32 @@ void findByVehicleIds_sendsTopParameter_whenMaxResultsProvided() { assertThat(predictions).hasSize(1); } + + @Test + void findByStopIds_collectionOverload_returnsPredictions_whenResponseContainsPredictions() { + this.server.stubFor(get(urlPathEqualTo("/bustime/api/v3/getpredictions")) + .withQueryParam("stpid", equalTo("456,789")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("bus/prediction/success.json")))); + + List predictions = this.api.findByStopIds(List.of("456", "789")); + + assertThat(predictions).hasSize(1); + } + + @Test + void findByVehicleIds_collectionOverload_returnsPredictions_whenResponseContainsPredictions() { + this.server.stubFor(get(urlPathEqualTo("/bustime/api/v3/getpredictions")) + .withQueryParam("vid", equalTo("509,510")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("bus/prediction/success.json")))); + + List predictions = this.api.findByVehicleIds(List.of("509", "510")); + + assertThat(predictions).hasSize(1); + } } diff --git a/src/test/java/com/cta4j/bus/route/RoutesApiImplTest.java b/src/test/java/com/cta4j/bus/route/RoutesApiImplTest.java index ff7b5c50..60e5751e 100644 --- a/src/test/java/com/cta4j/bus/route/RoutesApiImplTest.java +++ b/src/test/java/com/cta4j/bus/route/RoutesApiImplTest.java @@ -67,6 +67,19 @@ void list_returnsEmpty_whenResponseHasNoDataAndNoErrors() { assertThat(routes).isEmpty(); } + @Test + void list_returnsEmpty_whenRoutesIsExplicitlyEmptyArray() { + this.server.stubFor(get(urlPathEqualTo("/bustime/api/v3/getroutes")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("bus/route/empty_routes_array.json")))); + + List routes = this.api.list(); + + assertThat(routes).isEmpty(); + } + @Test void list_throwsCta4jBusException_whenResponseContainsFatalErrors() { this.server.stubFor(get(urlPathEqualTo("/bustime/api/v3/getroutes")) diff --git a/src/test/java/com/cta4j/bus/stop/StopMapperTest.java b/src/test/java/com/cta4j/bus/stop/StopMapperTest.java index f595e996..e97688d6 100644 --- a/src/test/java/com/cta4j/bus/stop/StopMapperTest.java +++ b/src/test/java/com/cta4j/bus/stop/StopMapperTest.java @@ -5,6 +5,8 @@ import com.cta4j.bus.stop.model.Stop; import org.junit.jupiter.api.Test; +import java.util.List; + import static org.assertj.core.api.Assertions.*; class StopMapperTest { @@ -37,4 +39,31 @@ void toDomain_mapsAdaAccessibleTrue() { assertThat(stop.adaAccessible()).isTrue(); } + + @Test + void toDomain_mapsDetoursAddedAndRemoved_whenNonNull() { + CtaStop wire = new CtaStop( + "456", + "Ashland & Division", + 41.9, + -87.67, + List.of(1, 2), + List.of(3), + 5, + null + ); + + Stop stop = StopMapper.INSTANCE.toDomain(wire); + + assertThat(stop.detoursAdded()).containsExactly(1, 2); + assertThat(stop.detoursRemoved()).containsExactly(3); + assertThat(stop.gtfsSequence()).isEqualTo(5); + } + + @Test + void toDomain_returnsNull_whenStopIsNull() { + Stop stop = StopMapper.INSTANCE.toDomain(null); + + assertThat(stop).isNull(); + } } diff --git a/src/test/java/com/cta4j/bus/vehicle/VehiclesApiImplTest.java b/src/test/java/com/cta4j/bus/vehicle/VehiclesApiImplTest.java index 91b300d5..34cc0d92 100644 --- a/src/test/java/com/cta4j/bus/vehicle/VehiclesApiImplTest.java +++ b/src/test/java/com/cta4j/bus/vehicle/VehiclesApiImplTest.java @@ -75,6 +75,19 @@ void findByIds_returnsEmpty_whenResponseHasNoDataAndNoErrors() { assertThat(vehicles).isEmpty(); } + @Test + void findByIds_returnsEmpty_whenVehicleIsExplicitlyEmptyArray() { + this.server.stubFor(get(urlPathEqualTo("/bustime/api/v3/getvehicles")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("bus/vehicle/empty_vehicle_array.json")))); + + List vehicles = this.api.findByIds(List.of("509")); + + assertThat(vehicles).isEmpty(); + } + @Test void findByIds_returnsEmpty_whenAllErrorsAreResourceSpecific() { this.server.stubFor(get(urlPathEqualTo("/bustime/api/v3/getvehicles")) diff --git a/src/test/java/com/cta4j/train/arrival/ArrivalsApiImplTest.java b/src/test/java/com/cta4j/train/arrival/ArrivalsApiImplTest.java index 49b282c7..6cb6b5e9 100644 --- a/src/test/java/com/cta4j/train/arrival/ArrivalsApiImplTest.java +++ b/src/test/java/com/cta4j/train/arrival/ArrivalsApiImplTest.java @@ -323,6 +323,24 @@ void findByMapId_throwsCta4jArrivalsException_withDefaultMessage_whenErrNmIsBlan .satisfies(e -> assertThat(((Cta4jArrivalsException) e).getRawErrorCode()).isEqualTo(1)); } + @Test + void findByMapId_throwsCta4jArrivalsException_withDefaultMessage_whenErrNmIsAbsent() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/ttarrivals.aspx")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody("{\"ctatt\":{\"tmst\":\"2015-04-30T20:23:53\",\"errCd\":\"1\"}}"))); + + MapArrivalsQuery query = MapArrivalsQuery.builder("40900").build(); + + assertThatThrownBy(() -> this.api.findByMapId(query)) + .isInstanceOf(Cta4jArrivalsException.class) + .hasMessage("An unknown error occurred.") + .satisfies(e -> assertThat(((Cta4jArrivalsException) e).getErrorCode()) + .isEqualTo(ArrivalsErrorCode.UNKNOWN)) + .satisfies(e -> assertThat(((Cta4jArrivalsException) e).getRawErrorCode()).isEqualTo(1)); + } + @Test void findByMapId_throwsCta4jArrivalsException_whenServerReturnsErrorStatus() { this.server.stubFor(get(urlPathEqualTo("/api/1.0/ttarrivals.aspx")) diff --git a/src/test/java/com/cta4j/train/follow/FollowApiImplTest.java b/src/test/java/com/cta4j/train/follow/FollowApiImplTest.java index 17fa05c6..ce8d55e8 100644 --- a/src/test/java/com/cta4j/train/follow/FollowApiImplTest.java +++ b/src/test/java/com/cta4j/train/follow/FollowApiImplTest.java @@ -178,6 +178,22 @@ void findByRun_throwsCta4jFollowException_withDefaultMessage_whenErrNmIsBlank() .satisfies(e -> assertThat(((Cta4jFollowException) e).getRawErrorCode()).isEqualTo(1)); } + @Test + void findByRun_throwsCta4jFollowException_withDefaultMessage_whenErrNmIsAbsent() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/ttfollow.aspx")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody("{\"ctatt\":{\"tmst\":\"2015-04-30T20:23:53\",\"errCd\":\"1\"}}"))); + + assertThatThrownBy(() -> this.api.findByRun("123")) + .isInstanceOf(Cta4jFollowException.class) + .hasMessage("An unknown error occurred.") + .satisfies(e -> assertThat(((Cta4jFollowException) e).getErrorCode()) + .isEqualTo(FollowErrorCode.UNKNOWN)) + .satisfies(e -> assertThat(((Cta4jFollowException) e).getRawErrorCode()).isEqualTo(1)); + } + @Test void findByRun_throwsCta4jFollowException_whenServerReturnsErrorStatus() { this.server.stubFor(get(urlPathEqualTo("/api/1.0/ttfollow.aspx")) diff --git a/src/test/java/com/cta4j/train/location/LocationsApiImplTest.java b/src/test/java/com/cta4j/train/location/LocationsApiImplTest.java index 52ec2f01..cfaca7b6 100644 --- a/src/test/java/com/cta4j/train/location/LocationsApiImplTest.java +++ b/src/test/java/com/cta4j/train/location/LocationsApiImplTest.java @@ -198,6 +198,22 @@ void findByLines_throwsCta4jLocationsException_withDefaultMessage_whenErrNmIsBla .satisfies(e -> assertThat(((Cta4jLocationsException) e).getRawErrorCode()).isEqualTo(1)); } + @Test + void findByLines_throwsCta4jLocationsException_withDefaultMessage_whenErrNmIsAbsent() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/ttpositions.aspx")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody("{\"ctatt\":{\"tmst\":\"2015-04-30T20:23:53\",\"errCd\":\"1\"}}"))); + + assertThatThrownBy(() -> this.api.findByLines(List.of(TrainLine.RED))) + .isInstanceOf(Cta4jLocationsException.class) + .hasMessage("An unknown error occurred.") + .satisfies(e -> assertThat(((Cta4jLocationsException) e).getErrorCode()) + .isEqualTo(LocationsErrorCode.UNKNOWN)) + .satisfies(e -> assertThat(((Cta4jLocationsException) e).getRawErrorCode()).isEqualTo(1)); + } + @Test void findByLines_throwsCta4jLocationsException_whenServerReturnsErrorStatus() { this.server.stubFor(get(urlPathEqualTo("/api/1.0/ttpositions.aspx")) diff --git a/src/test/resources/bus/detour/empty_dtrs_array.json b/src/test/resources/bus/detour/empty_dtrs_array.json new file mode 100644 index 00000000..cae8e0d9 --- /dev/null +++ b/src/test/resources/bus/detour/empty_dtrs_array.json @@ -0,0 +1,5 @@ +{ + "bustime-response": { + "dtrs": [] + } +} \ No newline at end of file diff --git a/src/test/resources/bus/direction/empty_directions_array.json b/src/test/resources/bus/direction/empty_directions_array.json new file mode 100644 index 00000000..6f88a064 --- /dev/null +++ b/src/test/resources/bus/direction/empty_directions_array.json @@ -0,0 +1,5 @@ +{ + "bustime-response": { + "directions": [] + } +} \ No newline at end of file diff --git a/src/test/resources/bus/locale/empty_locale_array.json b/src/test/resources/bus/locale/empty_locale_array.json new file mode 100644 index 00000000..5f97ace1 --- /dev/null +++ b/src/test/resources/bus/locale/empty_locale_array.json @@ -0,0 +1,5 @@ +{ + "bustime-response": { + "locale": [] + } +} \ No newline at end of file diff --git a/src/test/resources/bus/pattern/empty_ptr_array.json b/src/test/resources/bus/pattern/empty_ptr_array.json new file mode 100644 index 00000000..9c76151c --- /dev/null +++ b/src/test/resources/bus/pattern/empty_ptr_array.json @@ -0,0 +1,5 @@ +{ + "bustime-response": { + "ptr": [] + } +} \ No newline at end of file diff --git a/src/test/resources/bus/prediction/empty_prd_array.json b/src/test/resources/bus/prediction/empty_prd_array.json new file mode 100644 index 00000000..5786cc98 --- /dev/null +++ b/src/test/resources/bus/prediction/empty_prd_array.json @@ -0,0 +1,5 @@ +{ + "bustime-response": { + "prd": [] + } +} \ No newline at end of file diff --git a/src/test/resources/bus/route/empty_routes_array.json b/src/test/resources/bus/route/empty_routes_array.json new file mode 100644 index 00000000..da893d85 --- /dev/null +++ b/src/test/resources/bus/route/empty_routes_array.json @@ -0,0 +1,5 @@ +{ + "bustime-response": { + "routes": [] + } +} \ No newline at end of file diff --git a/src/test/resources/bus/vehicle/empty_vehicle_array.json b/src/test/resources/bus/vehicle/empty_vehicle_array.json new file mode 100644 index 00000000..973dcc3d --- /dev/null +++ b/src/test/resources/bus/vehicle/empty_vehicle_array.json @@ -0,0 +1,5 @@ +{ + "bustime-response": { + "vehicle": [] + } +} \ No newline at end of file From 2e0be0ccd7cf415434e8bc203550786d3596b5bc Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Tue, 28 Jul 2026 19:40:27 -0500 Subject: [PATCH 37/60] Markdown Javadoc comments --- src/main/java/com/cta4j/alert/AlertApi.java | 68 +++---- .../common/exception/Cta4jAlertException.java | 56 ++--- .../alert/common/model/AlertTrainLine.java | 82 +++----- .../cta4j/alert/common/model/ServiceType.java | 20 +- .../detailedalert/DetailedAlertsApi.java | 192 ++++++++---------- .../Cta4jDetailedAlertsException.java | 36 ++-- .../exception/DetailedAlertsErrorCode.java | 96 +++------ .../alert/detailedalert/model/Alert.java | 85 ++++---- .../detailedalert/model/ImpactedService.java | 53 +++-- .../alert/detailedalert/model/Severity.java | 40 ++-- .../detailedalert/query/AlertsQuery.java | 145 +++++-------- .../query/BusRouteAlertsQuery.java | 168 ++++++--------- .../detailedalert/query/LineAlertsQuery.java | 168 ++++++--------- .../query/StationAlertsQuery.java | 168 ++++++--------- .../exception/Cta4jRouteStatusException.java | 36 ++-- 15 files changed, 551 insertions(+), 862 deletions(-) diff --git a/src/main/java/com/cta4j/alert/AlertApi.java b/src/main/java/com/cta4j/alert/AlertApi.java index 890817e5..9a3b23c8 100644 --- a/src/main/java/com/cta4j/alert/AlertApi.java +++ b/src/main/java/com/cta4j/alert/AlertApi.java @@ -5,59 +5,45 @@ import com.cta4j.alert.routestatus.RouteStatusApi; import org.jspecify.annotations.NullMarked; -/** - * Primary entry point for interacting with the CTA Alerts API. - *

- * This interface provides grouped sub-APIs for different aspects of the CTA Alerts API, such as route status and - * detailed alerts. - *

- * Instances of {@code AlertApi} are immutable and thread-safe once built. - * Use {@link #builder()} to construct a configured instance. - */ +/// Primary entry point for interacting with the CTA Alerts API. +/// +/// This interface provides grouped sub-APIs for different aspects of the CTA Alerts API, such as route status and +/// detailed alerts. +/// +/// Instances of `AlertApi` are immutable and thread-safe once built. +/// Use [#builder()] to construct a configured instance. @NullMarked public interface AlertApi { - /** - * Provides access to route status-related endpoints. - * - * @return the {@link RouteStatusApi} - */ + /// Provides access to route status-related endpoints. + /// + /// @return the [RouteStatusApi] RouteStatusApi routeStatus(); - /** - * Provides access to detailed alert-related endpoints. - * - * @return the {@link DetailedAlertsApi} - */ + /// Provides access to detailed alert-related endpoints. + /// + /// @return the [DetailedAlertsApi] DetailedAlertsApi detailedAlerts(); - /** - * Builder for constructing {@link AlertApi} instances. - */ + /// Builder for constructing [AlertApi] instances. interface Builder { - /** - * Sets the API host to use for requests. - *

- * If not specified, the default CTA Alerts API host is used. - * - * @param host the API host - * @return this builder instance - * @throws NullPointerException if {@code host} is {@code null} - */ + /// Sets the API host to use for requests. + /// + /// If not specified, the default CTA Alerts API host is used. + /// + /// @param host the API host + /// @return this builder instance + /// @throws NullPointerException if `host` is `null` Builder host(String host); - /** - * Builds a configured {@link AlertApi} instance. - * - * @return a new {@link AlertApi} - */ + /// Builds a configured [AlertApi] instance. + /// + /// @return a new [AlertApi] AlertApi build(); } - /** - * Creates a new {@link Builder} for constructing a {@link AlertApi}. - * - * @return a new {@link Builder} - */ + /// Creates a new [Builder] for constructing a [AlertApi]. + /// + /// @return a new [Builder] static Builder builder() { return new AlertApiImpl.BuilderImpl(); } diff --git a/src/main/java/com/cta4j/alert/common/exception/Cta4jAlertException.java b/src/main/java/com/cta4j/alert/common/exception/Cta4jAlertException.java index f2b92e11..f6e2e376 100644 --- a/src/main/java/com/cta4j/alert/common/exception/Cta4jAlertException.java +++ b/src/main/java/com/cta4j/alert/common/exception/Cta4jAlertException.java @@ -4,63 +4,51 @@ import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; -/** - * A custom exception class for handling cta4j alert-specific errors. - */ +/// A custom exception class for handling cta4j alert-specific errors. @NullMarked public class Cta4jAlertException extends Cta4jException { - /** - * The raw error code associated with this exception, if available. - */ + /// The raw error code associated with this exception, if available. @Nullable private final Integer rawErrorCode; - /** - * Constructs a {@code Cta4jAlertException}. - * - * @param message the detail message - * @param endpoint the endpoint associated with the exception - * @throws NullPointerException if {@code endpoint} is {@code null} - */ + /// Constructs a `Cta4jAlertException`. + /// + /// @param message the detail message + /// @param endpoint the endpoint associated with the exception + /// @throws NullPointerException if `endpoint` is `null` public Cta4jAlertException(String message, String endpoint) { super(message, endpoint); this.rawErrorCode = null; } - /** - * Constructs a {@code Cta4jAlertException}. - * - * @param message the detail message - * @param endpoint the endpoint associated with the exception - * @param cause the cause of the exception - * @throws NullPointerException if {@code endpoint} is {@code null} - */ + /// Constructs a `Cta4jAlertException`. + /// + /// @param message the detail message + /// @param endpoint the endpoint associated with the exception + /// @param cause the cause of the exception + /// @throws NullPointerException if `endpoint` is `null` public Cta4jAlertException(String message, String endpoint, Throwable cause) { super(message, endpoint, cause); this.rawErrorCode = null; } - /** - * Constructs a {@code Cta4jAlertException} with a raw error code. - * - * @param message the detail message - * @param endpoint the endpoint associated with the exception - * @param rawErrorCode the raw error code associated with the exception - * @throws NullPointerException if {@code endpoint} is {@code null} - */ + /// Constructs a `Cta4jAlertException` with a raw error code. + /// + /// @param message the detail message + /// @param endpoint the endpoint associated with the exception + /// @param rawErrorCode the raw error code associated with the exception + /// @throws NullPointerException if `endpoint` is `null` public Cta4jAlertException(String message, String endpoint, int rawErrorCode) { super(message, endpoint); this.rawErrorCode = rawErrorCode; } - /** - * Returns the raw error code associated with this exception, if available. - * - * @return the raw error code, or {@code null} if not available - */ + /// Returns the raw error code associated with this exception, if available. + /// + /// @return the raw error code, or `null` if not available public @Nullable Integer getRawErrorCode() { return this.rawErrorCode; } diff --git a/src/main/java/com/cta4j/alert/common/model/AlertTrainLine.java b/src/main/java/com/cta4j/alert/common/model/AlertTrainLine.java index 656f6520..41319deb 100644 --- a/src/main/java/com/cta4j/alert/common/model/AlertTrainLine.java +++ b/src/main/java/com/cta4j/alert/common/model/AlertTrainLine.java @@ -4,91 +4,63 @@ import java.util.Objects; -/** - * Represents a train line as filterable through the CTA Alerts API. - *

- * Unlike the Train Tracker API, which has no concept of express service, the Alerts API treats the Purple Line - * Express as a distinct route designator ({@code "Pexp"}) from the regular Purple Line ({@code "P"}); per CTA's - * documentation, alerts affecting the Purple Line may be tagged with either designator, or both. - */ +/// Represents a train line as filterable through the CTA Alerts API. +/// +/// Unlike the Train Tracker API, which has no concept of express service, the Alerts API treats the Purple Line +/// Express as a distinct route designator (`"Pexp"`) from the regular Purple Line (`"P"`); per CTA's documentation, +/// alerts affecting the Purple Line may be tagged with either designator, or both. @NullMarked public enum AlertTrainLine { - /** - * Indicates the Red Line. - */ + /// Indicates the Red Line. RED("Red"), - /** - * Indicates the Blue Line. - */ + /// Indicates the Blue Line. BLUE("Blue"), - /** - * Indicates the Brown Line. - */ + /// Indicates the Brown Line. BROWN("Brn"), - /** - * Indicates the Green Line. - */ + /// Indicates the Green Line. GREEN("G"), - /** - * Indicates the Orange Line. - */ + /// Indicates the Orange Line. ORANGE("Org"), - /** - * Indicates the Purple Line, excluding express service. - */ + /// Indicates the Purple Line, excluding express service. PURPLE("P"), - /** - * Indicates the Purple Line Express. - */ + /// Indicates the Purple Line Express. PURPLE_EXPRESS("Pexp"), - /** - * Indicates the Pink Line. - */ + /// Indicates the Pink Line. PINK("Pink"), - /** - * Indicates the Yellow Line. - */ + /// Indicates the Yellow Line. YELLOW("Y"); - /** - * The CTA Alerts API route designator for this train line. - */ + /// The CTA Alerts API route designator for this train line. private final String code; - /** - * Constructs an {@code AlertTrainLine}. - * - * @param code the CTA Alerts API route designator of the train line - * @throws NullPointerException if {@code code} is {@code null} - */ + /// Constructs an `AlertTrainLine`. + /// + /// @param code the CTA Alerts API route designator of the train line + /// @throws NullPointerException if `code` is `null` AlertTrainLine(String code) { this.code = Objects.requireNonNull(code); } - /** - * Gets the CTA Alerts API route designator for this train line. - * - * @return the route designator - */ + /// Gets the CTA Alerts API route designator for this train line. + /// + /// @return the route designator public String getCode() { return this.code; } - /** - * Returns the {@code AlertTrainLine} corresponding to the given route designator. - * - * @param code the CTA Alerts API route designator of the train line (case-insensitive) - * @return the corresponding {@code AlertTrainLine} - * @throws IllegalArgumentException if the code does not correspond to any known train line - */ + /// Returns the `AlertTrainLine` corresponding to the given route designator. + /// + /// @param code the CTA Alerts API route designator of the train line (case-insensitive) + /// @return the corresponding `AlertTrainLine` + /// @throws IllegalArgumentException if the code does not correspond to any known train line public static AlertTrainLine fromCode(String code) { Objects.requireNonNull(code); diff --git a/src/main/java/com/cta4j/alert/common/model/ServiceType.java b/src/main/java/com/cta4j/alert/common/model/ServiceType.java index bbd1268b..fe787465 100644 --- a/src/main/java/com/cta4j/alert/common/model/ServiceType.java +++ b/src/main/java/com/cta4j/alert/common/model/ServiceType.java @@ -2,28 +2,18 @@ import org.jspecify.annotations.NullMarked; -/** - * Represents a category of CTA service - a bus route, train route, train station, or systemwide grouping. - */ +/// Represents a category of CTA service - a bus route, train route, train station, or systemwide grouping. @NullMarked public enum ServiceType { - /** - * Indicates bus routes. - */ + /// Indicates bus routes. BUS, - /** - * Indicates rail (train) routes. - */ + /// Indicates rail (train) routes. RAIL, - /** - * Indicates train stations. - */ + /// Indicates train stations. STATION, - /** - * Indicates systemwide categories, such as all routes, all bus routes, or all train routes. - */ + /// Indicates systemwide categories, such as all routes, all bus routes, or all train routes. SYSTEMWIDE } diff --git a/src/main/java/com/cta4j/alert/detailedalert/DetailedAlertsApi.java b/src/main/java/com/cta4j/alert/detailedalert/DetailedAlertsApi.java index 26dee936..6cc24046 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/DetailedAlertsApi.java +++ b/src/main/java/com/cta4j/alert/detailedalert/DetailedAlertsApi.java @@ -13,30 +13,23 @@ import java.util.List; import java.util.Objects; -/** - * Provides access to detailed alert-related endpoints of the CTA Alerts API. - *

- * This API allows retrieval of all alerts, or filtered by bus route ID, train line, or station ID. - */ +/// Provides access to detailed alert-related endpoints of the CTA Alerts API. +/// +/// This API allows retrieval of all alerts, or filtered by bus route ID, train line, or station ID. @NullMarked public interface DetailedAlertsApi { - /** - * Retrieves alerts matching the given query parameters. - * - * @param query the query parameters for fetching alerts - * @return a {@link List} of {@link Alert}s matching the query, or an empty {@link List} if no alerts are found - * @throws NullPointerException if {@code query} is {@code null} - * @throws Cta4jDetailedAlertsException if the API returns an error response or the response cannot be parsed - */ + /// Retrieves alerts matching the given query parameters. + /// + /// @param query the query parameters for fetching alerts + /// @return a [List] of [Alert]s matching the query, or an empty [List] if no alerts are found + /// @throws NullPointerException if `query` is `null` + /// @throws Cta4jDetailedAlertsException if the API returns an error response or the response cannot be parsed List list(AlertsQuery query); - /** - * Retrieves alerts using the default query parameters. - * - * @return a {@link List} of {@link Alert}s matching the default query, or an empty {@link List} if no alerts are - * found - * @throws Cta4jDetailedAlertsException if the API returns an error response or the response cannot be parsed - */ + /// Retrieves alerts using the default query parameters. + /// + /// @return a [List] of [Alert]s matching the default query, or an empty [List] if no alerts are found + /// @throws Cta4jDetailedAlertsException if the API returns an error response or the response cannot be parsed default List list() { AlertsQuery query = AlertsQuery.builder() .build(); @@ -44,31 +37,26 @@ default List list() { return this.list(query); } - /** - * Retrieves alerts by bus route IDs. - * - * @param query the query parameters for fetching alerts by bus route IDs - * @return a {@link List} of {@link Alert}s corresponding to the provided bus route IDs, or an empty {@link List} - * if no alerts are found - * @throws NullPointerException if {@code query} is {@code null} - * @throws IllegalArgumentException if any of the query's route IDs matches a train line code (e.g., "Red"); use - * {@link #findByLines(LineAlertsQuery)} instead - * @throws Cta4jDetailedAlertsException if the API returns an error response or the response cannot be parsed - */ + /// Retrieves alerts by bus route IDs. + /// + /// @param query the query parameters for fetching alerts by bus route IDs + /// @return a [List] of [Alert]s corresponding to the provided bus route IDs, + /// or an empty [List] if no alerts are found + /// @throws NullPointerException if `query` is `null` + /// @throws IllegalArgumentException if any of the query's route IDs matches a train line code (e.g., "Red"); + /// use [#findByLines(LineAlertsQuery)] instead + /// @throws Cta4jDetailedAlertsException if the API returns an error response or the response cannot be parsed List findByBusRouteIds(BusRouteAlertsQuery query); - /** - * Retrieves alerts by bus route IDs. - * - * @param routeIds a {@link Collection} of bus route IDs - * @return a {@link List} of {@link Alert}s corresponding to the provided bus route IDs, or an empty {@link List} - * if no alerts are found - * @throws NullPointerException if {@code routeIds} is {@code null}, or if any element of {@code routeIds} is - * {@code null} - * @throws IllegalArgumentException if any of the {@code routeIds} matches a train line code (e.g., "Red"); use - * {@link #findByLines(Collection)} instead - * @throws Cta4jDetailedAlertsException if the API returns an error response or the response cannot be parsed - */ + /// Retrieves alerts by bus route IDs. + /// + /// @param routeIds a [Collection] of bus route IDs + /// @return a [List] of [Alert]s corresponding to the provided bus route IDs, + /// or an empty [List] if no alerts are found + /// @throws NullPointerException if `routeIds` is `null`, or if any element of `routeIds` is `null` + /// @throws IllegalArgumentException if any of the `routeIds` matches a train line code (e.g., "Red"); + /// use [#findByLines(Collection)] instead + /// @throws Cta4jDetailedAlertsException if the API returns an error response or the response cannot be parsed default List findByBusRouteIds(Collection routeIds) { Objects.requireNonNull(routeIds); @@ -80,17 +68,15 @@ default List findByBusRouteIds(Collection routeIds) { return this.findByBusRouteIds(query); } - /** - * Retrieves alerts by bus route ID. - * - * @param routeId the bus route ID - * @return a {@link List} of {@link Alert}s corresponding to the provided bus route ID, or an empty {@link List} - * if no alerts are found - * @throws NullPointerException if {@code routeId} is {@code null} - * @throws IllegalArgumentException if {@code routeId} matches a train line code (e.g., "Red"); use - * {@link #findByLine(AlertTrainLine)} instead - * @throws Cta4jDetailedAlertsException if the API returns an error response or the response cannot be parsed - */ + /// Retrieves alerts by bus route ID. + /// + /// @param routeId the bus route ID + /// @return a [List] of [Alert]s corresponding to the provided bus route ID, + /// or an empty [List] if no alerts are found + /// @throws NullPointerException if `routeId` is `null` + /// @throws IllegalArgumentException if `routeId` matches a train line code (e.g., "Red"); + /// use [#findByLine(AlertTrainLine)] instead + /// @throws Cta4jDetailedAlertsException if the API returns an error response or the response cannot be parsed default List findByBusRouteId(String routeId) { Objects.requireNonNull(routeId); @@ -99,27 +85,22 @@ default List findByBusRouteId(String routeId) { return this.findByBusRouteIds(routeIds); } - /** - * Retrieves alerts by train lines. - * - * @param query the query parameters for fetching alerts by train lines - * @return a {@link List} of {@link Alert}s corresponding to the provided train lines, or an empty {@link List} - * if no alerts are found - * @throws NullPointerException if {@code query} is {@code null} - * @throws Cta4jDetailedAlertsException if the API returns an error response or the response cannot be parsed - */ + /// Retrieves alerts by train lines. + /// + /// @param query the query parameters for fetching alerts by train lines + /// @return a [List] of [Alert]s corresponding to the provided train lines, or an empty [List] if no alerts are + /// found + /// @throws NullPointerException if `query` is `null` + /// @throws Cta4jDetailedAlertsException if the API returns an error response or the response cannot be parsed List findByLines(LineAlertsQuery query); - /** - * Retrieves alerts by train lines. - * - * @param lines a {@link Collection} of train lines - * @return a {@link List} of {@link Alert}s corresponding to the provided train lines, or an empty {@link List} - * if no alerts are found - * @throws NullPointerException if {@code lines} is {@code null}, or if any element of {@code lines} is - * {@code null} - * @throws Cta4jDetailedAlertsException if the API returns an error response or the response cannot be parsed - */ + /// Retrieves alerts by train lines. + /// + /// @param lines a [Collection] of train lines + /// @return a [List] of [Alert]s corresponding to the provided train lines, or an empty [List] if no alerts are + /// found + /// @throws NullPointerException if `lines` is `null`, or if any element of `lines` is `null` + /// @throws Cta4jDetailedAlertsException if the API returns an error response or the response cannot be parsed default List findByLines(Collection lines) { Objects.requireNonNull(lines); @@ -131,15 +112,13 @@ default List findByLines(Collection lines) { return this.findByLines(query); } - /** - * Retrieves alerts by train line. - * - * @param line the train line - * @return a {@link List} of {@link Alert}s corresponding to the provided train line, or an empty {@link List} if - * no alerts are found - * @throws NullPointerException if {@code line} is {@code null} - * @throws Cta4jDetailedAlertsException if the API returns an error response or the response cannot be parsed - */ + /// Retrieves alerts by train line. + /// + /// @param line the train line + /// @return a [List] of [Alert]s corresponding to the provided train line, or an empty [List] if no alerts are + /// found + /// @throws NullPointerException if `line` is `null` + /// @throws Cta4jDetailedAlertsException if the API returns an error response or the response cannot be parsed default List findByLine(AlertTrainLine line) { Objects.requireNonNull(line); @@ -148,27 +127,22 @@ default List findByLine(AlertTrainLine line) { return this.findByLines(lines); } - /** - * Retrieves alerts by station IDs. - * - * @param query the query parameters for fetching alerts by station IDs - * @return a {@link List} of {@link Alert}s corresponding to the provided station IDs, or an empty {@link List} - * if no alerts are found - * @throws NullPointerException if {@code query} is {@code null} - * @throws Cta4jDetailedAlertsException if the API returns an error response or the response cannot be parsed - */ + /// Retrieves alerts by station IDs. + /// + /// @param query the query parameters for fetching alerts by station IDs + /// @return a [List] of [Alert]s corresponding to the provided station IDs, or an empty [List] if no alerts are + /// found + /// @throws NullPointerException if `query` is `null` + /// @throws Cta4jDetailedAlertsException if the API returns an error response or the response cannot be parsed List findByStationIds(StationAlertsQuery query); - /** - * Retrieves alerts by station IDs. - * - * @param stationIds a {@link Collection} of station IDs - * @return a {@link List} of {@link Alert}s corresponding to the provided station IDs, or an empty {@link List} - * if no alerts are found - * @throws NullPointerException if {@code stationIds} is {@code null}, or if any element of {@code stationIds} is - * {@code null} - * @throws Cta4jDetailedAlertsException if the API returns an error response or the response cannot be parsed - */ + /// Retrieves alerts by station IDs. + /// + /// @param stationIds a [Collection] of station IDs + /// @return a [List] of [Alert]s corresponding to the provided station IDs, or an empty [List] if no alerts are + /// found + /// @throws NullPointerException if `stationIds` is `null`, or if any element of `stationIds` is `null` + /// @throws Cta4jDetailedAlertsException if the API returns an error response or the response cannot be parsed default List findByStationIds(Collection stationIds) { Objects.requireNonNull(stationIds); @@ -180,15 +154,13 @@ default List findByStationIds(Collection stationIds) { return this.findByStationIds(query); } - /** - * Retrieves alerts by station ID. - * - * @param stationId the station ID - * @return a {@link List} of {@link Alert}s corresponding to the provided station ID, or an empty {@link List} if - * no alerts are found - * @throws NullPointerException if {@code stationId} is {@code null} - * @throws Cta4jDetailedAlertsException if the API returns an error response or the response cannot be parsed - */ + /// Retrieves alerts by station ID. + /// + /// @param stationId the station ID + /// @return a [List] of [Alert]s corresponding to the provided station ID, or an empty [List] if no alerts are + /// found + /// @throws NullPointerException if `stationId` is `null` + /// @throws Cta4jDetailedAlertsException if the API returns an error response or the response cannot be parsed default List findByStationId(String stationId) { Objects.requireNonNull(stationId); diff --git a/src/main/java/com/cta4j/alert/detailedalert/exception/Cta4jDetailedAlertsException.java b/src/main/java/com/cta4j/alert/detailedalert/exception/Cta4jDetailedAlertsException.java index d61a2e8d..f4ea3ccd 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/exception/Cta4jDetailedAlertsException.java +++ b/src/main/java/com/cta4j/alert/detailedalert/exception/Cta4jDetailedAlertsException.java @@ -5,46 +5,36 @@ import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; -/** - * A custom exception class for handling cta4j detailed alerts-specific errors. - */ +/// A custom exception class for handling cta4j detailed alerts-specific errors. @NullMarked public final class Cta4jDetailedAlertsException extends Cta4jAlertException { - /** - * The error code associated with this exception, if available. - */ + /// The error code associated with this exception, if available. @Nullable private final DetailedAlertsErrorCode errorCode; - /** - * Constructs a {@code Cta4jDetailedAlertsException}. - * - * @param message the detail message - * @param cause the cause of the exception - */ + /// Constructs a `Cta4jDetailedAlertsException`. + /// + /// @param message the detail message + /// @param cause the cause of the exception public Cta4jDetailedAlertsException(String message, Throwable cause) { super(message, AlertApiConstants.DETAILED_ALERTS_ENDPOINT, cause); this.errorCode = null; } - /** - * Constructs a {@code Cta4jDetailedAlertsException}. - * - * @param message the detail message - * @param rawErrorCode the raw error code associated with the exception - */ + /// Constructs a `Cta4jDetailedAlertsException`. + /// + /// @param message the detail message + /// @param rawErrorCode the raw error code associated with the exception public Cta4jDetailedAlertsException(String message, int rawErrorCode) { super(message, AlertApiConstants.DETAILED_ALERTS_ENDPOINT, rawErrorCode); this.errorCode = DetailedAlertsErrorCode.fromCode(rawErrorCode); } - /** - * Returns the error code associated with this exception, if available. - * - * @return the error code, or {@code null} if not available - */ + /// Returns the error code associated with this exception, if available. + /// + /// @return the error code, or `null` if not available public @Nullable DetailedAlertsErrorCode getErrorCode() { return this.errorCode; } diff --git a/src/main/java/com/cta4j/alert/detailedalert/exception/DetailedAlertsErrorCode.java b/src/main/java/com/cta4j/alert/detailedalert/exception/DetailedAlertsErrorCode.java index be9b6849..c8176c35 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/exception/DetailedAlertsErrorCode.java +++ b/src/main/java/com/cta4j/alert/detailedalert/exception/DetailedAlertsErrorCode.java @@ -2,113 +2,75 @@ import org.jspecify.annotations.NullMarked; -/** - * Represents the error codes returned by the CTA Detailed Alerts API. - */ +/// Represents the error codes returned by the CTA Detailed Alerts API. @NullMarked public enum DetailedAlertsErrorCode { - /** - * Indicates that the request was successful and there were no errors. - */ + /// Indicates that the request was successful and there were no errors. OK(0), - /** - * Indicates that there are no active alerts. - */ + /// Indicates that there are no active alerts. NO_ACTIVE_ALERTS(25), - /** - * Indicates that there are no active alerts based on the provided filter criteria. - */ + /// Indicates that there are no active alerts based on the provided filter criteria. NO_ACTIVE_ALERTS_FOR_FILTER(50), - /** - * Indicates that the provided "activeonly" value is invalid. - */ + /// Indicates that the provided "activeonly" value is invalid. INVALID_ACTIVEONLY(100), - /** - * Indicates that the provided "accessibility" value is invalid. - */ + /// Indicates that the provided "accessibility" value is invalid. INVALID_ACCESSIBILITY(101), - /** - * Indicates that the provided "planned" value is invalid. - */ + /// Indicates that the provided "planned" value is invalid. INVALID_PLANNED(102), - /** - * Indicates that the provided station ID is not an integer. - */ + /// Indicates that the provided station ID is not an integer. STATIONID_NOT_INTEGER(103), - /** - * Indicates that the provided "bystartdate" value is not a valid date in "yyyyMMdd" format. - */ + /// Indicates that the provided "bystartdate" value is not a valid date in "yyyyMMdd" format. INVALID_BYSTARTDATE(104), - /** - * Indicates that the provided "recentdays" value is not an integer. - */ + /// Indicates that the provided "recentdays" value is not an integer. RECENTDAYS_NOT_INTEGER(105), - /** - * Indicates that the "routeid" and "stationid" parameters were both provided, which is not allowed. - */ + /// Indicates that the "routeid" and "stationid" parameters were both provided, which is not allowed. ROUTEID_STATIONID_CONFLICT(106), - /** - * Indicates that the "recentdays" and "bystartdate" parameters were both provided, which is not allowed. - */ + /// Indicates that the "recentdays" and "bystartdate" parameters were both provided, which is not allowed. RECENTDAYS_BYSTARTDATE_CONFLICT(107), - /** - * Indicates that the query string contains a parameter that is not recognized by the API. The supported API - * parameters are "activeonly", "accessibility", "planned", "routeid", "stationid", "bystartdate", "recentdays", - * and "outputType". - */ + /// Indicates that the query string contains a parameter that is not recognized by the API. The supported API + /// parameters are "activeonly", "accessibility", "planned", "routeid", "stationid", "bystartdate", "recentdays", + /// and "outputType". INVALID_PARAMETER(500), - /** - * Indicates that the server encountered an unexpected error that prevented it from fulfilling the request. - */ + /// Indicates that the server encountered an unexpected error that prevented it from fulfilling the request. SERVER_ERROR(900), - /** - * Indicates that an unknown error occurred that does not match any of the defined error codes. - */ + /// Indicates that an unknown error occurred that does not match any of the defined error codes. UNKNOWN(-1); - /** - * The integer code associated with this error code. - */ + /// The integer code associated with this error code. private final int code; - /** - * Constructs a {@code DetailedAlertsErrorCode}. - * - * @param code the integer code associated with the error code - */ + /// Constructs a `DetailedAlertsErrorCode`. + /// + /// @param code the integer code associated with the error code DetailedAlertsErrorCode(int code) { this.code = code; } - /** - * Returns the integer code associated with this error code. - * - * @return the integer code - */ + /// Returns the integer code associated with this error code. + /// + /// @return the integer code public int getCode() { return this.code; } - /** - * Returns the {@code DetailedAlertsErrorCode} corresponding to the given integer code. - * - * @param code the integer code to look up - * @return the corresponding {@code DetailedAlertsErrorCode}, or {@code UNKNOWN} if the code does not match any - * defined error code - */ + /// Returns the `DetailedAlertsErrorCode` corresponding to the given integer code. + /// + /// @param code the integer code to look up + /// @return the corresponding `DetailedAlertsErrorCode`, or `UNKNOWN` if the code does not match any + /// defined error code public static DetailedAlertsErrorCode fromCode(int code) { return switch (code) { case 0 -> OK; diff --git a/src/main/java/com/cta4j/alert/detailedalert/model/Alert.java b/src/main/java/com/cta4j/alert/detailedalert/model/Alert.java index fba441bc..7ea5f724 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/model/Alert.java +++ b/src/main/java/com/cta4j/alert/detailedalert/model/Alert.java @@ -8,27 +8,25 @@ import java.util.List; import java.util.Objects; -/** - * Represents a detailed alert describing an event that affects one or more CTA services. - * - * @param id the unique ID of this alert (e.g., "115070") - * @param headline the headline of this alert - * @param shortDescription the short description of this alert - * @param fullDescription the full description of this alert - * @param severity the severity of this alert - * @param impact the descriptive text of the impact this alert has on service (e.g., "Elevator Status", - * "Bus Stop Relocation", "Planned Reroute") - * @param startTime the start time of this alert - * @param endTime the end time of this alert, or {@code null} if not known - * @param openEnded whether this alert is open-ended (has no known end time) - * @param major whether this alert is of major significance - * @param url the URL of this alert's detail page on transitchicago.com - * @param impactedServices the services impacted by this alert - * @param ttim an undocumented field returned by the CTA Alerts API; its meaning is not specified and its presence - * is not guaranteed, or {@code null} if not returned - * @param guid an undocumented field returned by the CTA Alerts API that appears to be a stable, globally unique - * identifier for this alert, distinct from {@link #id}, or {@code null} if not returned - */ +/// Represents a detailed alert describing an event that affects one or more CTA services. +/// +/// @param id the unique ID of this alert (e.g., "115070") +/// @param headline the headline of this alert +/// @param shortDescription the short description of this alert +/// @param fullDescription the full description of this alert +/// @param severity the severity of this alert +/// @param impact the descriptive text of the impact this alert has on service +/// (e.g., "Elevator Status", "Bus Stop Relocation", "Planned Reroute") +/// @param startTime the start time of this alert +/// @param endTime the end time of this alert, or `null` if not known +/// @param openEnded whether this alert is open-ended (has no known end time) +/// @param major whether this alert is of major significance +/// @param url the URL of this alert's detail page on transitchicago.com +/// @param impactedServices the services impacted by this alert +/// @param ttim an undocumented field returned by the CTA Alerts API; its meaning is not specified and its presence is +/// not guaranteed, or `null` if not returned +/// @param guid an undocumented field returned by the CTA Alerts API that appears to be a stable, globally unique +/// identifier for this alert, distinct from [#id], or `null` if not returned @NullMarked public record Alert( String id, @@ -46,30 +44,27 @@ public record Alert( @Nullable String ttim, @Nullable String guid ) { - /** - * Constructs an {@code Alert}. - * - * @param id the unique ID of the alert (e.g., "115070") - * @param headline the headline of the alert - * @param shortDescription the short description of the alert - * @param fullDescription the full description of the alert - * @param severity the severity of the alert - * @param impact the descriptive text of the impact the alert has on service (e.g., "Elevator Status", - * "Bus Stop Relocation", "Planned Reroute") - * @param startTime the start time of the alert - * @param endTime the end time of the alert, or {@code null} if not known - * @param openEnded whether the alert is open-ended (has no known end time) - * @param major whether the alert is of major significance - * @param url the URL of the alert's detail page on transitchicago.com - * @param impactedServices the services impacted by the alert - * @param ttim an undocumented field returned by the CTA Alerts API; its meaning is not specified and its - * presence is not guaranteed, or {@code null} if not returned - * @param guid an undocumented field returned by the CTA Alerts API that appears to be a stable, globally - * unique identifier for the alert, distinct from {@code id}, or {@code null} if not returned - * @throws NullPointerException if {@code id}, {@code headline}, {@code shortDescription}, - * {@code fullDescription}, {@code severity}, {@code impact}, {@code startTime}, {@code url}, or - * {@code impactedServices} is {@code null}, or if any element of {@code impactedServices} is {@code null} - */ + /// Constructs an `Alert`. + /// + /// @param id the unique ID of the alert (e.g., "115070") + /// @param headline the headline of the alert + /// @param shortDescription the short description of the alert + /// @param fullDescription the full description of the alert + /// @param severity the severity of the alert + /// @param impact the descriptive text of the impact the alert has on service + /// (e.g., "Elevator Status", "Bus Stop Relocation", "Planned Reroute") + /// @param startTime the start time of the alert + /// @param endTime the end time of the alert, or `null` if not known + /// @param openEnded whether the alert is open-ended (has no known end time) + /// @param major whether the alert is of major significance + /// @param url the URL of the alert's detail page on transitchicago.com + /// @param impactedServices the services impacted by the alert + /// @param ttim an undocumented field returned by the CTA Alerts API; its meaning is not specified and its presence + /// is not guaranteed, or `null` if not returned + /// @param guid an undocumented field returned by the CTA Alerts API that appears to be a stable, globally unique + /// identifier for the alert, distinct from `id`, or `null` if not returned + /// @throws NullPointerException if `id`, `headline`, `shortDescription`, `fullDescription`, `severity`, `impact`, + /// `startTime`, `url`, or `impactedServices` is `null`, or if any element of `impactedServices` is `null` public Alert { Objects.requireNonNull(id); Objects.requireNonNull(headline); diff --git a/src/main/java/com/cta4j/alert/detailedalert/model/ImpactedService.java b/src/main/java/com/cta4j/alert/detailedalert/model/ImpactedService.java index b23410af..9dbc6937 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/model/ImpactedService.java +++ b/src/main/java/com/cta4j/alert/detailedalert/model/ImpactedService.java @@ -6,20 +6,17 @@ import java.net.URI; import java.util.Objects; -/** - * Represents a single service - a bus route, train route, train station, or systemwide grouping - impacted by - * an alert. - * - * @param type the type of service this service represents - * @param typeDescription the plain English description of {@code type} (e.g., "Bus Route") - * @param name the name of this service (e.g., "Clark", "Red Line", "Jackson", "All Bus Routes") - * @param serviceId the identifier of this service; matches GTFS route or station IDs, except for systemwide groupings, - * which use a fixed identifier instead (e.g., "22", "Red", "Systemwide") - * @param color the color of this service used in maps, as {@code rrggbb} (e.g., "565a5c") - * @param textColor the suggested color of text displayed against {@code color}; casing varies (e.g., "ffffff", - * "FFFFFF") - * @param url the URL of this service's page on transitchicago.com - */ +/// Represents a single service - a bus route, train route, train station, or systemwide grouping - impacted by an +/// alert. +/// +/// @param type the type of service this service represents +/// @param typeDescription the plain English description of `type` (e.g., "Bus Route") +/// @param name the name of this service (e.g., "Clark", "Red Line", "Jackson", "All Bus Routes") +/// @param serviceId the identifier of this service; matches GTFS route or station IDs, except for systemwide +/// groupings, which use a fixed identifier instead (e.g., "22", "Red", "Systemwide") +/// @param color the color of this service used in maps, as `rrggbb` (e.g., "565a5c") +/// @param textColor the suggested color of text displayed against `color`; casing varies (e.g., "ffffff", "FFFFFF") +/// @param url the URL of this service's page on transitchicago.com @NullMarked public record ImpactedService( ServiceType type, @@ -30,21 +27,19 @@ public record ImpactedService( String textColor, URI url ) { - /** - * Constructs an {@code ImpactedService}. - * - * @param type the type of service the service represents - * @param typeDescription the plain English description of {@code type} (e.g., "Bus Route") - * @param name the name of the service (e.g., "Clark", "Red Line", "Jackson", "All Bus Routes") - * @param serviceId the identifier of the service; matches GTFS route or station IDs, except for systemwide - * groupings, which use a fixed identifier instead (e.g., "22", "Red", "Systemwide") - * @param color the color of the service used in maps, as {@code rrggbb} (e.g., "565a5c") - * @param textColor the suggested color of text displayed against {@code color}; casing varies (e.g., "ffffff", - * "FFFFFF") - * @param url the URL of the service's page on transitchicago.com - * @throws NullPointerException if {@code type}, {@code typeDescription}, {@code name}, {@code serviceId}, - * {@code color}, {@code textColor}, or {@code url} is {@code null} - */ + /// Constructs an `ImpactedService`. + /// + /// @param type the type of service the service represents + /// @param typeDescription the plain English description of `type` (e.g., "Bus Route") + /// @param name the name of the service (e.g., "Clark", "Red Line", "Jackson", "All Bus Routes") + /// @param serviceId the identifier of the service; matches GTFS route or station IDs, except for systemwide + /// groupings, which use a fixed identifier instead (e.g., "22", "Red", "Systemwide") + /// @param color the color of the service used in maps, as `rrggbb` (e.g., "565a5c") + /// @param textColor the suggested color of text displayed against `color`; casing varies + /// (e.g., "ffffff", "FFFFFF") + /// @param url the URL of the service's page on transitchicago.com + /// @throws NullPointerException if `type`, `typeDescription`, `name`, `serviceId`, `color`, `textColor`, or `url` + /// is `null` public ImpactedService { Objects.requireNonNull(type); Objects.requireNonNull(typeDescription); diff --git a/src/main/java/com/cta4j/alert/detailedalert/model/Severity.java b/src/main/java/com/cta4j/alert/detailedalert/model/Severity.java index dd3c3ae9..7e39e3fe 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/model/Severity.java +++ b/src/main/java/com/cta4j/alert/detailedalert/model/Severity.java @@ -4,34 +4,30 @@ import java.util.Objects; -/** - * Represents the severity of an alert. - * - * @param score the numerical score used to rank this severity, based on the alert's impact on overall service, between - * 0 and 99 (inclusive) - * @param color the hexadecimal RGB color code used to color this severity's text on transitchicago.com; length and - * casing vary (e.g., "000000", "06c", "B45F04") - * @param css the category used to pick the icon and display style of the alert; not limited to the four documented - * values (e.g., "normal", "planned", "minor", "major", "special-note") - */ +/// Represents the severity of an alert. +/// +/// @param score the numerical score used to rank this severity, based on the alert's impact on overall service, +/// between 0 and 99 (inclusive) +/// @param color the hexadecimal RGB color code used to color this severity's text on transitchicago.com; length and +/// casing vary (e.g., "000000", "06c", "B45F04") +/// @param css the category used to pick the icon and display style of the alert; not limited to the four documented +/// values (e.g., "normal", "planned", "minor", "major", "special-note") @NullMarked public record Severity( int score, String color, String css ) { - /** - * Constructs a {@code Severity}. - * - * @param score the numerical score used to rank the severity, based on the alert's impact on overall - * service, between 0 and 99 (inclusive) - * @param color the hexadecimal RGB color code used to color the severity's text on transitchicago.com; length and - * casing vary (e.g., "000000", "06c", "B45F04") - * @param css the category used to pick the icon and display style of the alert; not limited to the four - * documented values (e.g., "normal", "planned", "minor", "major", "special-note") - * @throws NullPointerException if {@code color} or {@code css} is {@code null} - * @throws IllegalArgumentException if {@code score} is not between 0 and 99 (inclusive) - */ + /// Constructs a `Severity`. + /// + /// @param score the numerical score used to rank the severity, based on the alert's impact on overall service, + /// between 0 and 99 (inclusive) + /// @param color the hexadecimal RGB color code used to color the severity's text on transitchicago.com; length and + /// casing vary (e.g., "000000", "06c", "B45F04") + /// @param css the category used to pick the icon and display style of the alert; not limited to the four + /// documented values (e.g., "normal", "planned", "minor", "major", "special-note") + /// @throws NullPointerException if `color` or `css` is `null` + /// @throws IllegalArgumentException if `score` is not between 0 and 99 (inclusive) public Severity { Objects.requireNonNull(color); Objects.requireNonNull(css); diff --git a/src/main/java/com/cta4j/alert/detailedalert/query/AlertsQuery.java b/src/main/java/com/cta4j/alert/detailedalert/query/AlertsQuery.java index ba1db206..7b227ce7 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/query/AlertsQuery.java +++ b/src/main/java/com/cta4j/alert/detailedalert/query/AlertsQuery.java @@ -6,16 +6,13 @@ import java.time.LocalDate; import java.util.Objects; -/** - * Represents a query for detailed alerts. - * - * @param activeOnly whether to include only alerts that are currently active - * @param accessibility whether to include alerts that affect accessible paths in stations - * @param planned whether to include common planned alerts - * @param byStartDate the optional date; only alerts with a start date before this date are included - * @param recentDays the optional number of days; only alerts that started within this many days of today are - * included - */ +/// Represents a query for detailed alerts. +/// +/// @param activeOnly whether to include only alerts that are currently active +/// @param accessibility whether to include alerts that affect accessible paths in stations +/// @param planned whether to include common planned alerts +/// @param byStartDate the optional date; only alerts with a start date before this date are included +/// @param recentDays the optional number of days; only alerts that started within this many days of today are included @NullMarked public record AlertsQuery( boolean activeOnly, @@ -24,18 +21,16 @@ public record AlertsQuery( @Nullable LocalDate byStartDate, @Nullable Integer recentDays ) { - /** - * Constructs an {@code AlertsQuery}. - * - * @param activeOnly whether to include only alerts that are currently active - * @param accessibility whether to include alerts that affect accessible paths in stations - * @param planned whether to include common planned alerts - * @param byStartDate the optional date; only alerts with a start date before this date are included - * @param recentDays the optional number of days; only alerts that started within this many days of today are - * included - * @throws IllegalArgumentException if both {@code byStartDate} and {@code recentDays} are specified, or if - * {@code recentDays} is non-{@code null} and not positive - */ + /// Constructs an `AlertsQuery`. + /// + /// @param activeOnly whether to include only alerts that are currently active + /// @param accessibility whether to include alerts that affect accessible paths in stations + /// @param planned whether to include common planned alerts + /// @param byStartDate the optional date; only alerts with a start date before this date are included + /// @param recentDays the optional number of days; only alerts that started within this many days of today are + /// included + /// @throws IllegalArgumentException if both `byStartDate` and `recentDays` are specified, or if `recentDays` is + /// non-`null` and not positive public AlertsQuery { if (byStartDate != null && recentDays != null) { throw new IllegalArgumentException("byStartDate and recentDays cannot both be specified"); @@ -46,101 +41,77 @@ public record AlertsQuery( } } - /** - * Creates a builder for {@code AlertsQuery}. - * - * @return a new {@code Builder} instance - */ + /// Creates a builder for `AlertsQuery`. + /// + /// @return a new `Builder` instance public static Builder builder() { return new Builder(); } - /** - * A builder for {@code AlertsQuery}. - */ + /// A builder for `AlertsQuery`. public static final class Builder { - /** - * Whether to include only alerts that are currently active. - */ + /// Whether to include only alerts that are currently active. private boolean activeOnly; - /** - * Whether to include alerts that affect accessible paths in stations. - */ + /// Whether to include alerts that affect accessible paths in stations. private boolean accessibility; - /** - * Whether to include common planned alerts. - */ + /// Whether to include common planned alerts. private boolean planned; - /** - * The optional date; only alerts with a start date before this date are included. - */ + /// The optional date; only alerts with a start date before this date are included. @Nullable private LocalDate byStartDate; - /** - * The optional number of days; only alerts that started within this many days of today are included. - */ + /// The optional number of days; only alerts that started within this many days of today are included. @Nullable private Integer recentDays; - /** - * Constructs a {@code Builder}. - *

- * By default, {@code activeOnly} is {@code false}, and {@code accessibility} and {@code planned} are - * {@code true}, matching the CTA Alerts API's own defaults. - */ + /// Constructs a `Builder`. + /// + /// By default, `activeOnly` is `false`, and `accessibility` and `planned` are `true`, matching the CTA Alerts + /// API's own defaults. public Builder() { this.activeOnly = false; this.accessibility = true; this.planned = true; } - /** - * Sets whether to include only alerts that are currently active. - * - * @param activeOnly whether to include only active alerts - * @return this {@code Builder} instance - */ + /// Sets whether to include only alerts that are currently active. + /// + /// @param activeOnly whether to include only active alerts + /// @return this `Builder` instance public Builder activeOnly(boolean activeOnly) { this.activeOnly = activeOnly; return this; } - /** - * Sets whether to include alerts that affect accessible paths in stations. - * - * @param accessibility whether to include accessibility-related alerts - * @return this {@code Builder} instance - */ + /// Sets whether to include alerts that affect accessible paths in stations. + /// + /// @param accessibility whether to include accessibility-related alerts + /// @return this `Builder` instance public Builder accessibility(boolean accessibility) { this.accessibility = accessibility; return this; } - /** - * Sets whether to include common planned alerts. - * - * @param planned whether to include planned alerts - * @return this {@code Builder} instance - */ + /// Sets whether to include common planned alerts. + /// + /// @param planned whether to include planned alerts + /// @return this `Builder` instance public Builder planned(boolean planned) { this.planned = planned; return this; } - /** - * Sets the date; only alerts with a start date before this date are included. - * - * @param byStartDate the date to filter alerts by - * @return this {@code Builder} instance - * @throws NullPointerException if {@code byStartDate} is {@code null} - */ + /// Sets the date; only alerts with a start date before this date are included. + /// + /// @param byStartDate the date to filter alerts by + /// @return this `Builder` instance + /// @throws NullPointerException if `byStartDate` is `null` public Builder byStartDate(LocalDate byStartDate) { Objects.requireNonNull(byStartDate); @@ -149,13 +120,11 @@ public Builder byStartDate(LocalDate byStartDate) { return this; } - /** - * Sets the number of days; only alerts that started within this many days of today are included. - * - * @param recentDays the number of days to filter alerts by - * @return this {@code Builder} instance - * @throws IllegalArgumentException if {@code recentDays} is not positive - */ + /// Sets the number of days; only alerts that started within this many days of today are included. + /// + /// @param recentDays the number of days to filter alerts by + /// @return this `Builder` instance + /// @throws IllegalArgumentException if `recentDays` is not positive public Builder recentDays(int recentDays) { if (recentDays <= 0) { throw new IllegalArgumentException("recentDays must be positive"); @@ -166,12 +135,10 @@ public Builder recentDays(int recentDays) { return this; } - /** - * Builds the {@code AlertsQuery}. - * - * @return a new {@code AlertsQuery} instance - * @throws IllegalArgumentException if both {@code byStartDate} and {@code recentDays} were specified - */ + /// Builds the `AlertsQuery`. + /// + /// @return a new `AlertsQuery` instance + /// @throws IllegalArgumentException if both `byStartDate` and `recentDays` were specified public AlertsQuery build() { return new AlertsQuery( this.activeOnly, diff --git a/src/main/java/com/cta4j/alert/detailedalert/query/BusRouteAlertsQuery.java b/src/main/java/com/cta4j/alert/detailedalert/query/BusRouteAlertsQuery.java index 0ed26cf9..1ac16557 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/query/BusRouteAlertsQuery.java +++ b/src/main/java/com/cta4j/alert/detailedalert/query/BusRouteAlertsQuery.java @@ -8,17 +8,14 @@ import java.util.List; import java.util.Objects; -/** - * Represents a query for detailed bus route alerts. - * - * @param routeIds the {@link List} of bus route IDs to retrieve alerts for - * @param activeOnly whether to include only alerts that are currently active - * @param accessibility whether to include alerts that affect accessible paths in stations - * @param planned whether to include common planned alerts - * @param byStartDate the optional date; only alerts with a start date before this date are included - * @param recentDays the optional number of days; only alerts that started within this many days of today are - * included - */ +/// Represents a query for detailed bus route alerts. +/// +/// @param routeIds the [List] of bus route IDs to retrieve alerts for +/// @param activeOnly whether to include only alerts that are currently active +/// @param accessibility whether to include alerts that affect accessible paths in stations +/// @param planned whether to include common planned alerts +/// @param byStartDate the optional date; only alerts with a start date before this date are included +/// @param recentDays the optional number of days; only alerts that started within this many days of today are included @NullMarked public record BusRouteAlertsQuery( List routeIds, @@ -28,21 +25,18 @@ public record BusRouteAlertsQuery( @Nullable LocalDate byStartDate, @Nullable Integer recentDays ) { - /** - * Constructs a {@code BusRouteAlertsQuery}. - * - * @param routeIds the {@link List} of bus route IDs to retrieve alerts for - * @param activeOnly whether to include only alerts that are currently active - * @param accessibility whether to include alerts that affect accessible paths in stations - * @param planned whether to include common planned alerts - * @param byStartDate the optional date; only alerts with a start date before this date are included - * @param recentDays the optional number of days; only alerts that started within this many days of today are - * included - * @throws NullPointerException if {@code routeIds} is {@code null}, or if any element of {@code routeIds} is - * {@code null} - * @throws IllegalArgumentException if both {@code byStartDate} and {@code recentDays} are specified, or if - * {@code recentDays} is non-{@code null} and not positive - */ + /// Constructs a `BusRouteAlertsQuery`. + /// + /// @param routeIds the [List] of bus route IDs to retrieve alerts for + /// @param activeOnly whether to include only alerts that are currently active + /// @param accessibility whether to include alerts that affect accessible paths in stations + /// @param planned whether to include common planned alerts + /// @param byStartDate the optional date; only alerts with a start date before this date are included + /// @param recentDays the optional number of days; only alerts that started within this many days of today are + /// included + /// @throws NullPointerException if `routeIds` is `null`, or if any element of `routeIds` is `null` + /// @throws IllegalArgumentException if both `byStartDate` and `recentDays` are specified, or if `recentDays` is + /// non-`null` and not positive public BusRouteAlertsQuery { Objects.requireNonNull(routeIds); @@ -57,64 +51,44 @@ public record BusRouteAlertsQuery( } } - /** - * Creates a builder for {@code BusRouteAlertsQuery}. - * - * @param routeIds the {@link Collection} of bus route IDs to retrieve alerts for - * @return a new {@code Builder} instance - * @throws NullPointerException if {@code routeIds} is {@code null}, or if any element of {@code routeIds} is - * {@code null} - */ + /// Creates a builder for `BusRouteAlertsQuery`. + /// + /// @param routeIds the [Collection] of bus route IDs to retrieve alerts for + /// @return a new `Builder` instance + /// @throws NullPointerException if `routeIds` is `null`, or if any element of `routeIds` is `null` public static Builder builder(Collection routeIds) { return new Builder(routeIds); } - /** - * A builder for {@code BusRouteAlertsQuery}. - */ + /// A builder for `BusRouteAlertsQuery`. public static final class Builder { - /** - * The {@link List} of bus route IDs to retrieve alerts for. - */ + /// The [List] of bus route IDs to retrieve alerts for. private final List routeIds; - /** - * Whether to include only alerts that are currently active. - */ + /// Whether to include only alerts that are currently active. private boolean activeOnly; - /** - * Whether to include alerts that affect accessible paths in stations. - */ + /// Whether to include alerts that affect accessible paths in stations. private boolean accessibility; - /** - * Whether to include common planned alerts. - */ + /// Whether to include common planned alerts. private boolean planned; - /** - * The optional date; only alerts with a start date before this date are included. - */ + /// The optional date; only alerts with a start date before this date are included. @Nullable private LocalDate byStartDate; - /** - * The optional number of days; only alerts that started within this many days of today are included. - */ + /// The optional number of days; only alerts that started within this many days of today are included. @Nullable private Integer recentDays; - /** - * Constructs a {@code Builder}. - *

- * By default, {@code activeOnly} is {@code false}, and {@code accessibility} and {@code planned} are - * {@code true}, matching the CTA Alerts API's own defaults. - * - * @param routeIds the {@link Collection} of bus route IDs to retrieve alerts for - * @throws NullPointerException if {@code routeIds} is {@code null}, or if any element of {@code routeIds} is - * {@code null} - */ + /// Constructs a `Builder`. + /// + /// By default, `activeOnly` is `false`, and `accessibility` and `planned` are `true`, matching the CTA Alerts + /// API's own defaults. + /// + /// @param routeIds the [Collection] of bus route IDs to retrieve alerts for + /// @throws NullPointerException if `routeIds` is `null`, or if any element of `routeIds` is `null` public Builder(Collection routeIds) { Objects.requireNonNull(routeIds); @@ -124,49 +98,41 @@ public Builder(Collection routeIds) { this.planned = true; } - /** - * Sets whether to include only alerts that are currently active. - * - * @param activeOnly whether to include only active alerts - * @return this {@code Builder} instance - */ + /// Sets whether to include only alerts that are currently active. + /// + /// @param activeOnly whether to include only active alerts + /// @return this `Builder` instance public Builder activeOnly(boolean activeOnly) { this.activeOnly = activeOnly; return this; } - /** - * Sets whether to include alerts that affect accessible paths in stations. - * - * @param accessibility whether to include accessibility-related alerts - * @return this {@code Builder} instance - */ + /// Sets whether to include alerts that affect accessible paths in stations. + /// + /// @param accessibility whether to include accessibility-related alerts + /// @return this `Builder` instance public Builder accessibility(boolean accessibility) { this.accessibility = accessibility; return this; } - /** - * Sets whether to include common planned alerts. - * - * @param planned whether to include planned alerts - * @return this {@code Builder} instance - */ + /// Sets whether to include common planned alerts. + /// + /// @param planned whether to include planned alerts + /// @return this `Builder` instance public Builder planned(boolean planned) { this.planned = planned; return this; } - /** - * Sets the date; only alerts with a start date before this date are included. - * - * @param byStartDate the date to filter alerts by - * @return this {@code Builder} instance - * @throws NullPointerException if {@code byStartDate} is {@code null} - */ + /// Sets the date; only alerts with a start date before this date are included. + /// + /// @param byStartDate the date to filter alerts by + /// @return this `Builder` instance + /// @throws NullPointerException if `byStartDate` is `null` public Builder byStartDate(LocalDate byStartDate) { Objects.requireNonNull(byStartDate); @@ -175,13 +141,11 @@ public Builder byStartDate(LocalDate byStartDate) { return this; } - /** - * Sets the number of days; only alerts that started within this many days of today are included. - * - * @param recentDays the number of days to filter alerts by - * @return this {@code Builder} instance - * @throws IllegalArgumentException if {@code recentDays} is not positive - */ + /// Sets the number of days; only alerts that started within this many days of today are included. + /// + /// @param recentDays the number of days to filter alerts by + /// @return this `Builder` instance + /// @throws IllegalArgumentException if `recentDays` is not positive public Builder recentDays(int recentDays) { if (recentDays <= 0) { throw new IllegalArgumentException("recentDays must be positive"); @@ -192,12 +156,10 @@ public Builder recentDays(int recentDays) { return this; } - /** - * Builds the {@code BusRouteAlertsQuery}. - * - * @return a new {@code BusRouteAlertsQuery} instance - * @throws IllegalArgumentException if both {@code byStartDate} and {@code recentDays} were specified - */ + /// Builds the `BusRouteAlertsQuery`. + /// + /// @return a new `BusRouteAlertsQuery` instance + /// @throws IllegalArgumentException if both `byStartDate` and `recentDays` were specified public BusRouteAlertsQuery build() { return new BusRouteAlertsQuery( this.routeIds, diff --git a/src/main/java/com/cta4j/alert/detailedalert/query/LineAlertsQuery.java b/src/main/java/com/cta4j/alert/detailedalert/query/LineAlertsQuery.java index 6911a138..b8dae78d 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/query/LineAlertsQuery.java +++ b/src/main/java/com/cta4j/alert/detailedalert/query/LineAlertsQuery.java @@ -9,17 +9,14 @@ import java.util.List; import java.util.Objects; -/** - * Represents a query for detailed train line alerts. - * - * @param lines the {@link List} of {@link AlertTrainLine}s to retrieve alerts for - * @param activeOnly whether to include only alerts that are currently active - * @param accessibility whether to include alerts that affect accessible paths in stations - * @param planned whether to include common planned alerts - * @param byStartDate the optional date; only alerts with a start date before this date are included - * @param recentDays the optional number of days; only alerts that started within this many days of today are - * included - */ +/// Represents a query for detailed train line alerts. +/// +/// @param lines the [List] of [AlertTrainLine]s to retrieve alerts for +/// @param activeOnly whether to include only alerts that are currently active +/// @param accessibility whether to include alerts that affect accessible paths in stations +/// @param planned whether to include common planned alerts +/// @param byStartDate the optional date; only alerts with a start date before this date are included +/// @param recentDays the optional number of days; only alerts that started within this many days of today are included @NullMarked public record LineAlertsQuery( List lines, @@ -29,21 +26,18 @@ public record LineAlertsQuery( @Nullable LocalDate byStartDate, @Nullable Integer recentDays ) { - /** - * Constructs a {@code LineAlertsQuery}. - * - * @param lines the {@link List} of {@link AlertTrainLine}s to retrieve alerts for - * @param activeOnly whether to include only alerts that are currently active - * @param accessibility whether to include alerts that affect accessible paths in stations - * @param planned whether to include common planned alerts - * @param byStartDate the optional date; only alerts with a start date before this date are included - * @param recentDays the optional number of days; only alerts that started within this many days of today are - * included - * @throws NullPointerException if {@code lines} is {@code null}, or if any element of {@code lines} is - * {@code null} - * @throws IllegalArgumentException if both {@code byStartDate} and {@code recentDays} are specified, or if - * {@code recentDays} is non-{@code null} and not positive - */ + /// Constructs a `LineAlertsQuery`. + /// + /// @param lines the [List] of [AlertTrainLine]s to retrieve alerts for + /// @param activeOnly whether to include only alerts that are currently active + /// @param accessibility whether to include alerts that affect accessible paths in stations + /// @param planned whether to include common planned alerts + /// @param byStartDate the optional date; only alerts with a start date before this date are included + /// @param recentDays the optional number of days; only alerts that started within this many days of today are + /// included + /// @throws NullPointerException if `lines` is `null`, or if any element of `lines` is `null` + /// @throws IllegalArgumentException if both `byStartDate` and `recentDays` are specified, or if `recentDays` is + /// non-`null` and not positive public LineAlertsQuery { Objects.requireNonNull(lines); @@ -58,64 +52,44 @@ public record LineAlertsQuery( } } - /** - * Creates a builder for {@code LineAlertsQuery}. - * - * @param lines the {@link Collection} of {@link AlertTrainLine}s to retrieve alerts for - * @return a new {@code Builder} instance - * @throws NullPointerException if {@code lines} is {@code null}, or if any element of {@code lines} is - * {@code null} - */ + /// Creates a builder for `LineAlertsQuery`. + /// + /// @param lines the [Collection] of [AlertTrainLine]s to retrieve alerts for + /// @return a new `Builder` instance + /// @throws NullPointerException if `lines` is `null`, or if any element of `lines` is `null` public static Builder builder(Collection lines) { return new Builder(lines); } - /** - * A builder for {@code LineAlertsQuery}. - */ + /// A builder for `LineAlertsQuery`. public static final class Builder { - /** - * The {@link List} of {@link AlertTrainLine}s to retrieve alerts for. - */ + /// The [List] of [AlertTrainLine]s to retrieve alerts for. private final List lines; - /** - * Whether to include only alerts that are currently active. - */ + /// Whether to include only alerts that are currently active. private boolean activeOnly; - /** - * Whether to include alerts that affect accessible paths in stations. - */ + /// Whether to include alerts that affect accessible paths in stations. private boolean accessibility; - /** - * Whether to include common planned alerts. - */ + /// Whether to include common planned alerts. private boolean planned; - /** - * The optional date; only alerts with a start date before this date are included. - */ + /// The optional date; only alerts with a start date before this date are included. @Nullable private LocalDate byStartDate; - /** - * The optional number of days; only alerts that started within this many days of today are included. - */ + /// The optional number of days; only alerts that started within this many days of today are included. @Nullable private Integer recentDays; - /** - * Constructs a {@code Builder}. - *

- * By default, {@code activeOnly} is {@code false}, and {@code accessibility} and {@code planned} are - * {@code true}, matching the CTA Alerts API's own defaults. - * - * @param lines the {@link Collection} of {@link AlertTrainLine}s to retrieve alerts for - * @throws NullPointerException if {@code lines} is {@code null}, or if any element of {@code lines} is - * {@code null} - */ + /// Constructs a `Builder`. + /// + /// By default, `activeOnly` is `false`, and `accessibility` and `planned` are `true`, matching the CTA Alerts + /// API's own defaults. + /// + /// @param lines the [Collection] of [AlertTrainLine]s to retrieve alerts for + /// @throws NullPointerException if `lines` is `null`, or if any element of `lines` is `null` public Builder(Collection lines) { Objects.requireNonNull(lines); @@ -125,49 +99,41 @@ public Builder(Collection lines) { this.planned = true; } - /** - * Sets whether to include only alerts that are currently active. - * - * @param activeOnly whether to include only active alerts - * @return this {@code Builder} instance - */ + /// Sets whether to include only alerts that are currently active. + /// + /// @param activeOnly whether to include only active alerts + /// @return this `Builder` instance public Builder activeOnly(boolean activeOnly) { this.activeOnly = activeOnly; return this; } - /** - * Sets whether to include alerts that affect accessible paths in stations. - * - * @param accessibility whether to include accessibility-related alerts - * @return this {@code Builder} instance - */ + /// Sets whether to include alerts that affect accessible paths in stations. + /// + /// @param accessibility whether to include accessibility-related alerts + /// @return this `Builder` instance public Builder accessibility(boolean accessibility) { this.accessibility = accessibility; return this; } - /** - * Sets whether to include common planned alerts. - * - * @param planned whether to include planned alerts - * @return this {@code Builder} instance - */ + /// Sets whether to include common planned alerts. + /// + /// @param planned whether to include planned alerts + /// @return this `Builder` instance public Builder planned(boolean planned) { this.planned = planned; return this; } - /** - * Sets the date; only alerts with a start date before this date are included. - * - * @param byStartDate the date to filter alerts by - * @return this {@code Builder} instance - * @throws NullPointerException if {@code byStartDate} is {@code null} - */ + /// Sets the date; only alerts with a start date before this date are included. + /// + /// @param byStartDate the date to filter alerts by + /// @return this `Builder` instance + /// @throws NullPointerException if `byStartDate` is `null` public Builder byStartDate(LocalDate byStartDate) { Objects.requireNonNull(byStartDate); @@ -176,13 +142,11 @@ public Builder byStartDate(LocalDate byStartDate) { return this; } - /** - * Sets the number of days; only alerts that started within this many days of today are included. - * - * @param recentDays the number of days to filter alerts by - * @return this {@code Builder} instance - * @throws IllegalArgumentException if {@code recentDays} is not positive - */ + /// Sets the number of days; only alerts that started within this many days of today are included. + /// + /// @param recentDays the number of days to filter alerts by + /// @return this `Builder` instance + /// @throws IllegalArgumentException if `recentDays` is not positive public Builder recentDays(int recentDays) { if (recentDays <= 0) { throw new IllegalArgumentException("recentDays must be positive"); @@ -193,12 +157,10 @@ public Builder recentDays(int recentDays) { return this; } - /** - * Builds the {@code LineAlertsQuery}. - * - * @return a new {@code LineAlertsQuery} instance - * @throws IllegalArgumentException if both {@code byStartDate} and {@code recentDays} were specified - */ + /// Builds the `LineAlertsQuery`. + /// + /// @return a new `LineAlertsQuery` instance + /// @throws IllegalArgumentException if both `byStartDate` and `recentDays` were specified public LineAlertsQuery build() { return new LineAlertsQuery( this.lines, diff --git a/src/main/java/com/cta4j/alert/detailedalert/query/StationAlertsQuery.java b/src/main/java/com/cta4j/alert/detailedalert/query/StationAlertsQuery.java index 5c4bdda9..70ef881b 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/query/StationAlertsQuery.java +++ b/src/main/java/com/cta4j/alert/detailedalert/query/StationAlertsQuery.java @@ -8,17 +8,14 @@ import java.util.List; import java.util.Objects; -/** - * Represents a query for detailed train station alerts. - * - * @param stationIds the {@link List} of train station IDs to retrieve alerts for - * @param activeOnly whether to include only alerts that are currently active - * @param accessibility whether to include alerts that affect accessible paths in stations - * @param planned whether to include common planned alerts - * @param byStartDate the optional date; only alerts with a start date before this date are included - * @param recentDays the optional number of days; only alerts that started within this many days of today are - * included - */ +/// Represents a query for detailed train station alerts. +/// +/// @param stationIds the [List] of train station IDs to retrieve alerts for +/// @param activeOnly whether to include only alerts that are currently active +/// @param accessibility whether to include alerts that affect accessible paths in stations +/// @param planned whether to include common planned alerts +/// @param byStartDate the optional date; only alerts with a start date before this date are included +/// @param recentDays the optional number of days; only alerts that started within this many days of today are included @NullMarked public record StationAlertsQuery( List stationIds, @@ -28,21 +25,18 @@ public record StationAlertsQuery( @Nullable LocalDate byStartDate, @Nullable Integer recentDays ) { - /** - * Constructs a {@code StationAlertsQuery}. - * - * @param stationIds the {@link List} of train station IDs to retrieve alerts for - * @param activeOnly whether to include only alerts that are currently active - * @param accessibility whether to include alerts that affect accessible paths in stations - * @param planned whether to include common planned alerts - * @param byStartDate the optional date; only alerts with a start date before this date are included - * @param recentDays the optional number of days; only alerts that started within this many days of today are - * included - * @throws NullPointerException if {@code stationIds} is {@code null}, or if any element of {@code stationIds} is - * {@code null} - * @throws IllegalArgumentException if both {@code byStartDate} and {@code recentDays} are specified, or if - * {@code recentDays} is non-{@code null} and not positive - */ + /// Constructs a `StationAlertsQuery`. + /// + /// @param stationIds the [List] of train station IDs to retrieve alerts for + /// @param activeOnly whether to include only alerts that are currently active + /// @param accessibility whether to include alerts that affect accessible paths in stations + /// @param planned whether to include common planned alerts + /// @param byStartDate the optional date; only alerts with a start date before this date are included + /// @param recentDays the optional number of days; only alerts that started within this many days of today are + /// included + /// @throws NullPointerException if `stationIds` is `null`, or if any element of `stationIds` is `null` + /// @throws IllegalArgumentException if both `byStartDate` and `recentDays` are specified, or if `recentDays` is + /// non-`null` and not positive public StationAlertsQuery { Objects.requireNonNull(stationIds); @@ -57,64 +51,44 @@ public record StationAlertsQuery( } } - /** - * Creates a builder for {@code StationAlertsQuery}. - * - * @param stationIds the {@link Collection} of train station IDs to retrieve alerts for - * @return a new {@code Builder} instance - * @throws NullPointerException if {@code stationIds} is {@code null}, or if any element of {@code stationIds} is - * {@code null} - */ + /// Creates a builder for `StationAlertsQuery`. + /// + /// @param stationIds the [Collection] of train station IDs to retrieve alerts for + /// @return a new `Builder` instance + /// @throws NullPointerException if `stationIds` is `null`, or if any element of `stationIds` is `null` public static Builder builder(Collection stationIds) { return new Builder(stationIds); } - /** - * A builder for {@code StationAlertsQuery}. - */ + /// A builder for `StationAlertsQuery`. public static final class Builder { - /** - * The {@link List} of train station IDs to retrieve alerts for. - */ + /// The [List] of train station IDs to retrieve alerts for. private final List stationIds; - /** - * Whether to include only alerts that are currently active. - */ + /// Whether to include only alerts that are currently active. private boolean activeOnly; - /** - * Whether to include alerts that affect accessible paths in stations. - */ + /// Whether to include alerts that affect accessible paths in stations. private boolean accessibility; - /** - * Whether to include common planned alerts. - */ + /// Whether to include common planned alerts. private boolean planned; - /** - * The optional date; only alerts with a start date before this date are included. - */ + /// The optional date; only alerts with a start date before this date are included. @Nullable private LocalDate byStartDate; - /** - * The optional number of days; only alerts that started within this many days of today are included. - */ + /// The optional number of days; only alerts that started within this many days of today are included. @Nullable private Integer recentDays; - /** - * Constructs a {@code Builder}. - *

- * By default, {@code activeOnly} is {@code false}, and {@code accessibility} and {@code planned} are - * {@code true}, matching the CTA Alerts API's own defaults. - * - * @param stationIds the {@link Collection} of train station IDs to retrieve alerts for - * @throws NullPointerException if {@code stationIds} is {@code null}, or if any element of {@code stationIds} - * is {@code null} - */ + /// Constructs a `Builder`. + /// + /// By default, `activeOnly` is `false`, and `accessibility` and `planned` are `true`, matching the CTA Alerts + /// API's own defaults. + /// + /// @param stationIds the [Collection] of train station IDs to retrieve alerts for + /// @throws NullPointerException if `stationIds` is `null`, or if any element of `stationIds` is `null` public Builder(Collection stationIds) { Objects.requireNonNull(stationIds); @@ -124,49 +98,41 @@ public Builder(Collection stationIds) { this.planned = true; } - /** - * Sets whether to include only alerts that are currently active. - * - * @param activeOnly whether to include only active alerts - * @return this {@code Builder} instance - */ + /// Sets whether to include only alerts that are currently active. + /// + /// @param activeOnly whether to include only active alerts + /// @return this `Builder` instance public Builder activeOnly(boolean activeOnly) { this.activeOnly = activeOnly; return this; } - /** - * Sets whether to include alerts that affect accessible paths in stations. - * - * @param accessibility whether to include accessibility-related alerts - * @return this {@code Builder} instance - */ + /// Sets whether to include alerts that affect accessible paths in stations. + /// + /// @param accessibility whether to include accessibility-related alerts + /// @return this `Builder` instance public Builder accessibility(boolean accessibility) { this.accessibility = accessibility; return this; } - /** - * Sets whether to include common planned alerts. - * - * @param planned whether to include planned alerts - * @return this {@code Builder} instance - */ + /// Sets whether to include common planned alerts. + /// + /// @param planned whether to include planned alerts + /// @return this `Builder` instance public Builder planned(boolean planned) { this.planned = planned; return this; } - /** - * Sets the date; only alerts with a start date before this date are included. - * - * @param byStartDate the date to filter alerts by - * @return this {@code Builder} instance - * @throws NullPointerException if {@code byStartDate} is {@code null} - */ + /// Sets the date; only alerts with a start date before this date are included. + /// + /// @param byStartDate the date to filter alerts by + /// @return this `Builder` instance + /// @throws NullPointerException if `byStartDate` is `null` public Builder byStartDate(LocalDate byStartDate) { Objects.requireNonNull(byStartDate); @@ -175,13 +141,11 @@ public Builder byStartDate(LocalDate byStartDate) { return this; } - /** - * Sets the number of days; only alerts that started within this many days of today are included. - * - * @param recentDays the number of days to filter alerts by - * @return this {@code Builder} instance - * @throws IllegalArgumentException if {@code recentDays} is not positive - */ + /// Sets the number of days; only alerts that started within this many days of today are included. + /// + /// @param recentDays the number of days to filter alerts by + /// @return this `Builder` instance + /// @throws IllegalArgumentException if `recentDays` is not positive public Builder recentDays(int recentDays) { if (recentDays <= 0) { throw new IllegalArgumentException("recentDays must be positive"); @@ -192,12 +156,10 @@ public Builder recentDays(int recentDays) { return this; } - /** - * Builds the {@code StationAlertsQuery}. - * - * @return a new {@code StationAlertsQuery} instance - * @throws IllegalArgumentException if both {@code byStartDate} and {@code recentDays} were specified - */ + /// Builds the `StationAlertsQuery`. + /// + /// @return a new `StationAlertsQuery` instance + /// @throws IllegalArgumentException if both `byStartDate` and `recentDays` were specified public StationAlertsQuery build() { return new StationAlertsQuery( this.stationIds, diff --git a/src/main/java/com/cta4j/alert/routestatus/exception/Cta4jRouteStatusException.java b/src/main/java/com/cta4j/alert/routestatus/exception/Cta4jRouteStatusException.java index d82c5659..a6a88738 100644 --- a/src/main/java/com/cta4j/alert/routestatus/exception/Cta4jRouteStatusException.java +++ b/src/main/java/com/cta4j/alert/routestatus/exception/Cta4jRouteStatusException.java @@ -5,46 +5,36 @@ import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; -/** - * A custom exception class for handling cta4j alert route status-specific errors. - */ +/// A custom exception class for handling cta4j alert route status-specific errors. @NullMarked public final class Cta4jRouteStatusException extends Cta4jAlertException { - /** - * The error code associated with this exception, if available. - */ + /// The error code associated with this exception, if available. @Nullable private final RouteStatusErrorCode errorCode; - /** - * Constructs a {@code Cta4jRouteStatusException}. - * - * @param message the detail message - * @param cause the cause of the exception - */ + /// Constructs a `Cta4jRouteStatusException`. + /// + /// @param message the detail message + /// @param cause the cause of the exception public Cta4jRouteStatusException(String message, Throwable cause) { super(message, AlertApiConstants.ROUTE_STATUS_ENDPOINT, cause); this.errorCode = null; } - /** - * Constructs a {@code Cta4jRouteStatusException}. - * - * @param message the detail message - * @param rawErrorCode the raw error code associated with the exception - */ + /// Constructs a `Cta4jRouteStatusException`. + /// + /// @param message the detail message + /// @param rawErrorCode the raw error code associated with the exception public Cta4jRouteStatusException(String message, int rawErrorCode) { super(message, AlertApiConstants.ROUTE_STATUS_ENDPOINT, rawErrorCode); this.errorCode = RouteStatusErrorCode.fromCode(rawErrorCode); } - /** - * Returns the error code associated with this exception, if available. - * - * @return the error code, or {@code null} if not available - */ + /// Returns the error code a ssociated with this exception, if available. + /// + /// @return the error code, or `null` if not available public @Nullable RouteStatusErrorCode getErrorCode() { return this.errorCode; } From a62295e1d76624615a1ccd528a74e675b279ea55 Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Wed, 29 Jul 2026 13:32:06 -0500 Subject: [PATCH 38/60] Markdown Javadoc comments --- src/main/java/com/cta4j/alert/AlertApi.java | 4 +- .../alert/routestatus/RouteStatusApi.java | 140 ++++++++---------- .../exception/Cta4jRouteStatusException.java | 2 +- .../exception/RouteStatusErrorCode.java | 84 ++++------- .../alert/routestatus/model/RouteStatus.java | 55 ++++--- src/main/java/com/cta4j/bus/BusApi.java | 131 +++++++--------- .../common/exception/Cta4jBusException.java | 49 +++--- .../java/com/cta4j/bus/detour/DetoursApi.java | 53 +++---- .../com/cta4j/bus/detour/model/Detour.java | 57 ++++--- .../detour/model/DetourRouteDirection.java | 22 ++- .../cta4j/bus/direction/DirectionsApi.java | 24 ++- .../java/com/cta4j/bus/locale/LocalesApi.java | 47 +++--- .../bus/locale/model/SupportedLocale.java | 22 ++- .../com/cta4j/bus/pattern/PatternsApi.java | 60 ++++---- .../cta4j/bus/pattern/model/PatternPoint.java | 42 +++--- .../bus/pattern/model/PatternPointType.java | 12 +- .../cta4j/bus/pattern/model/RoutePattern.java | 42 +++--- .../cta4j/bus/prediction/PredictionsApi.java | 124 +++++++--------- .../bus/prediction/model/DynamicAction.java | 112 +++++--------- .../cta4j/bus/prediction/model/FlagStop.java | 40 ++--- .../bus/prediction/model/PassengerLoad.java | 20 +-- .../bus/prediction/model/Prediction.java | 88 +++++------ .../prediction/model/PredictionMetadata.java | 80 +++++----- .../bus/prediction/model/PredictionType.java | 12 +- .../query/StopPredictionsQuery.java | 106 +++++-------- .../query/VehiclePredictionsQuery.java | 85 ++++------- .../java/com/cta4j/bus/route/RoutesApi.java | 18 +-- .../java/com/cta4j/bus/route/model/Route.java | 46 +++--- .../java/com/cta4j/bus/stop/StopsApi.java | 61 ++++---- .../java/com/cta4j/bus/stop/model/Stop.java | 56 ++++--- .../com/cta4j/bus/vehicle/VehiclesApi.java | 77 +++++----- .../cta4j/bus/vehicle/model/TransitMode.java | 44 ++---- .../com/cta4j/bus/vehicle/model/Vehicle.java | 39 +++-- .../bus/vehicle/model/VehicleMetadata.java | 114 +++++++------- 34 files changed, 798 insertions(+), 1170 deletions(-) diff --git a/src/main/java/com/cta4j/alert/AlertApi.java b/src/main/java/com/cta4j/alert/AlertApi.java index 9a3b23c8..7139e28f 100644 --- a/src/main/java/com/cta4j/alert/AlertApi.java +++ b/src/main/java/com/cta4j/alert/AlertApi.java @@ -10,8 +10,8 @@ /// This interface provides grouped sub-APIs for different aspects of the CTA Alerts API, such as route status and /// detailed alerts. /// -/// Instances of `AlertApi` are immutable and thread-safe once built. -/// Use [#builder()] to construct a configured instance. +/// Instances of `AlertApi` are immutable and thread-safe once built. Use [#builder()] to construct a configured +/// instance. @NullMarked public interface AlertApi { /// Provides access to route status-related endpoints. diff --git a/src/main/java/com/cta4j/alert/routestatus/RouteStatusApi.java b/src/main/java/com/cta4j/alert/routestatus/RouteStatusApi.java index 26f7746e..3041484d 100644 --- a/src/main/java/com/cta4j/alert/routestatus/RouteStatusApi.java +++ b/src/main/java/com/cta4j/alert/routestatus/RouteStatusApi.java @@ -10,42 +10,34 @@ import java.util.List; import java.util.Objects; -/** - * Provides access to route status-related endpoints of the CTA Alerts API. - *

- * This API allows retrieval of the status of all bus and train routes, or filtered by service type, bus route ID, - * train line, or station ID. - */ +/// Provides access to route status-related endpoints of the CTA Alerts API. +/// +/// This API allows retrieval of the status of all bus and train routes, or filtered by service type, bus route ID, +/// train line, or station ID. @NullMarked public interface RouteStatusApi { - /** - * Retrieves the status of all bus and train routes. - * - * @return a {@link List} of {@link RouteStatus}es, or an empty {@link List} if no route statuses are found - * @throws Cta4jRouteStatusException if the API returns an error response or the response cannot be parsed - */ + /// Retrieves the status of all bus and train routes. + /// + /// @return a [List] of [RouteStatus]es, or an empty [List] if no route statuses are found + /// @throws Cta4jRouteStatusException if the API returns an error response or the response cannot be parsed List list(); - /** - * Retrieves route statuses by their service types. - * - * @param types a {@link Collection} of service types - * @return a {@link List} of {@link RouteStatus}es corresponding to the provided types, or an empty {@link List} - * if no route statuses are found - * @throws NullPointerException if {@code types} is {@code null} or contains {@code null} elements - * @throws Cta4jRouteStatusException if the API returns an error response or the response cannot be parsed - */ + /// Retrieves route statuses by their service types. + /// + /// @param types a [Collection] of service types + /// @return a [List] of [RouteStatus]es corresponding to the provided types, or an empty [List] if no route + /// statuses are found + /// @throws NullPointerException if `types` is `null` or contains `null` elements + /// @throws Cta4jRouteStatusException if the API returns an error response or the response cannot be parsed List findByTypes(Collection types); - /** - * Retrieves route statuses by a service type. - * - * @param type the service type - * @return a {@link List} of {@link RouteStatus}es corresponding to the provided type, or an empty {@link List} - * if no route statuses are found - * @throws NullPointerException if {@code type} is {@code null} - * @throws Cta4jRouteStatusException if the API returns an error response or the response cannot be parsed - */ + /// Retrieves route statuses by a service type. + /// + /// @param type the service type + /// @return a [List] of [RouteStatus]es corresponding to the provided type, or an empty [List] if no route statuses + /// are found + /// @throws NullPointerException if `type` is `null` + /// @throws Cta4jRouteStatusException if the API returns an error response or the response cannot be parsed default List findByType(ServiceType type) { Objects.requireNonNull(type); @@ -54,30 +46,26 @@ default List findByType(ServiceType type) { return this.findByTypes(types); } - /** - * Retrieves route statuses for the specified bus route IDs. - * - * @param routeIds a {@link Collection} of bus route IDs - * @return a {@link List} of {@link RouteStatus}es associated with the bus route IDs, or an empty {@link List} if - * no route statuses are found for the bus route IDs - * @throws NullPointerException if {@code routeIds} is {@code null} or contains {@code null} elements - * @throws IllegalArgumentException if any of the {@code routeIds} matches a train line code (e.g., "Red"); use - * {@link #findByLines(Collection)} instead - * @throws Cta4jRouteStatusException if the API returns an error response or the response cannot be parsed - */ + /// Retrieves route statuses for the specified bus route IDs. + /// + /// @param routeIds a [Collection] of bus route IDs + /// @return a [List] of [RouteStatus]es associated with the bus route IDs, or an empty [List] if no route statuses + /// are found for the bus route IDs + /// @throws NullPointerException if `routeIds` is `null` or contains `null` elements + /// @throws IllegalArgumentException if any of the `routeIds` matches a train line code (e.g., "Red"); + /// use [#findByLines(Collection)] instead + /// @throws Cta4jRouteStatusException if the API returns an error response or the response cannot be parsed List findByBusRouteIds(Collection routeIds); - /** - * Retrieves route statuses for the specified bus route ID. - * - * @param routeId the bus route ID - * @return a {@link List} of {@link RouteStatus}es associated with the bus route ID, or an empty {@link List} if - * no route statuses are found for the bus route ID - * @throws NullPointerException if {@code routeId} is {@code null} - * @throws IllegalArgumentException if {@code routeId} matches a train line code (e.g., "Red"); use - * {@link #findByLine(AlertTrainLine)} instead - * @throws Cta4jRouteStatusException if the API returns an error response or the response cannot be parsed - */ + /// Retrieves route statuses for the specified bus route ID. + /// + /// @param routeId the bus route ID + /// @return a [List] of [RouteStatus]es associated with the bus route ID, or an empty [List] if no route statuses + /// are found for the bus route ID + /// @throws NullPointerException if `routeId` is `null` + /// @throws IllegalArgumentException if `routeId` matches a train line code (e.g., "Red"); + /// use [#findByLine(AlertTrainLine)] instead + /// @throws Cta4jRouteStatusException if the API returns an error response or the response cannot be parsed default List findByBusRouteId(String routeId) { Objects.requireNonNull(routeId); @@ -86,26 +74,22 @@ default List findByBusRouteId(String routeId) { return this.findByBusRouteIds(routeIds); } - /** - * Retrieves route statuses for the specified train lines. - * - * @param lines a {@link Collection} of train lines - * @return a {@link List} of {@link RouteStatus}es associated with the train lines, or an empty {@link List} - * if no route statuses are found for the train lines - * @throws NullPointerException if {@code lines} is {@code null} or contains {@code null} elements - * @throws Cta4jRouteStatusException if the API returns an error response or the response cannot be parsed - */ + /// Retrieves route statuses for the specified train lines. + /// + /// @param lines a [Collection] of train lines + /// @return a [List] of [RouteStatus]es associated with the train lines, or an empty [List] if no route statuses + /// are found for the train lines + /// @throws NullPointerException if `lines` is `null` or contains `null` elements + /// @throws Cta4jRouteStatusException if the API returns an error response or the response cannot be parsed List findByLines(Collection lines); - /** - * Retrieves route statuses for the specified train line. - * - * @param line the train line - * @return a {@link List} of {@link RouteStatus}es associated with the train line, or an empty {@link List} - * if no route statuses are found for the train line - * @throws NullPointerException if {@code line} is {@code null} - * @throws Cta4jRouteStatusException if the API returns an error response or the response cannot be parsed - */ + /// Retrieves route statuses for the specified train line. + /// + /// @param line the train line + /// @return a [List] of [RouteStatus]es associated with the train line, or an empty [List] if no route statuses are + /// found for the train line + /// @throws NullPointerException if `line` is `null` + /// @throws Cta4jRouteStatusException if the API returns an error response or the response cannot be parsed default List findByLine(AlertTrainLine line) { Objects.requireNonNull(line); @@ -114,14 +98,12 @@ default List findByLine(AlertTrainLine line) { return this.findByLines(lines); } - /** - * Retrieves route statuses for the specified station ID. - * - * @param stationId the station ID - * @return a {@link List} of {@link RouteStatus}es associated with the station ID, or an empty {@link List} if - * no route statuses are found for the station ID - * @throws NullPointerException if {@code stationId} is {@code null} - * @throws Cta4jRouteStatusException if the API returns an error response or the response cannot be parsed - */ + /// Retrieves route statuses for the specified station ID. + /// + /// @param stationId the station ID + /// @return a [List] of [RouteStatus]es associated with the station ID, or an empty [List] if no route statuses are + /// found for the station ID + /// @throws NullPointerException if `stationId` is `null` + /// @throws Cta4jRouteStatusException if the API returns an error response or the response cannot be parsed List findByStationId(String stationId); } diff --git a/src/main/java/com/cta4j/alert/routestatus/exception/Cta4jRouteStatusException.java b/src/main/java/com/cta4j/alert/routestatus/exception/Cta4jRouteStatusException.java index a6a88738..f87e6a78 100644 --- a/src/main/java/com/cta4j/alert/routestatus/exception/Cta4jRouteStatusException.java +++ b/src/main/java/com/cta4j/alert/routestatus/exception/Cta4jRouteStatusException.java @@ -32,7 +32,7 @@ public Cta4jRouteStatusException(String message, int rawErrorCode) { this.errorCode = RouteStatusErrorCode.fromCode(rawErrorCode); } - /// Returns the error code a ssociated with this exception, if available. + /// Returns the error code associated with this exception, if available. /// /// @return the error code, or `null` if not available public @Nullable RouteStatusErrorCode getErrorCode() { diff --git a/src/main/java/com/cta4j/alert/routestatus/exception/RouteStatusErrorCode.java b/src/main/java/com/cta4j/alert/routestatus/exception/RouteStatusErrorCode.java index b55b6d5d..c1f58291 100644 --- a/src/main/java/com/cta4j/alert/routestatus/exception/RouteStatusErrorCode.java +++ b/src/main/java/com/cta4j/alert/routestatus/exception/RouteStatusErrorCode.java @@ -2,95 +2,65 @@ import org.jspecify.annotations.NullMarked; -/** - * Represents the error codes returned by the CTA Route Status API. - */ +/// Represents the error codes returned by the CTA Route Status API. @NullMarked public enum RouteStatusErrorCode { - /** - * Indicates that the request was successful and there were no errors. - */ + /// Indicates that the request was successful and there were no errors. OK(0), - /** - * Indicates that no routes or stations matched the provided filter criteria. - *

- * This code is not documented in the CTA Alerts API documentation for the Route Status API, but has been - * observed in practice. - */ + /// Indicates that no routes or stations matched the provided filter criteria. + /// + /// This code is not documented in the CTA Alerts API documentation for the Route Status API, but has been observed + /// in practice. NO_RESULTS(50), - /** - * Indicates that the provided station ID is not an integer. - */ + /// Indicates that the provided station ID is not an integer. STATIONID_NOT_INTEGER(100), - /** - * Indicates that the provided service type is invalid. - */ + /// Indicates that the provided service type is invalid. INVALID_TYPE(101), - /** - * Indicates that the "routeid" and "stationid" parameters were both provided, which is not allowed. - */ + /// Indicates that the "routeid" and "stationid" parameters were both provided, which is not allowed. ROUTEID_STATIONID_CONFLICT(102), - /** - * Indicates that the "routeid" and "type" parameters were both provided, which is not allowed. - */ + /// Indicates that the "routeid" and "type" parameters were both provided, which is not allowed. ROUTEID_TYPE_CONFLICT(103), - /** - * Indicates that the "stationid" and "type" parameters were both provided, which is not allowed. - */ + /// Indicates that the "stationid" and "type" parameters were both provided, which is not allowed. STATIONID_TYPE_CONFLICT(104), - /** - * Indicates that the query string contains a parameter that is not recognized by the API. The supported API - * parameters are "type", "routeid", "stationid", and "outputType". - */ + /// Indicates that the query string contains a parameter that is not recognized by the API. The supported API + /// parameters are "type", "routeid", "stationid", and "outputType". INVALID_PARAMETER(500), - /** - * Indicates that the server encountered an unexpected error that prevented it from fulfilling the request. - */ + /// Indicates that the server encountered an unexpected error that prevented it from fulfilling the request. SERVER_ERROR(900), - /** - * Indicates that an unknown error occurred that does not match any of the defined error codes. - */ + /// Indicates that an unknown error occurred that does not match any of the defined error codes. UNKNOWN(-1); - /** - * The integer code associated with this error code. - */ + /// The integer code associated with this error code. private final int code; - /** - * Constructs a {@code RouteStatusErrorCode}. - * - * @param code the integer code associated with the error code - */ + /// Constructs a `RouteStatusErrorCode`. + /// + /// @param code the integer code associated with the error code RouteStatusErrorCode(int code) { this.code = code; } - /** - * Returns the integer code associated with this error code. - * - * @return the integer code - */ + /// Returns the integer code associated with this error code. + /// + /// @return the integer code public int getCode() { return this.code; } - /** - * Returns the {@code RouteStatusErrorCode} corresponding to the given integer code. - * - * @param code the integer code to look up - * @return the corresponding {@code RouteStatusErrorCode}, or {@code UNKNOWN} if the code does not match any - * defined error code - */ + /// Returns the `RouteStatusErrorCode` corresponding to the given integer code. + /// + /// @param code the integer code to look up + /// @return the corresponding `RouteStatusErrorCode`, or `UNKNOWN` if the code does not match any defined error + /// code public static RouteStatusErrorCode fromCode(int code) { return switch (code) { case 0 -> OK; diff --git a/src/main/java/com/cta4j/alert/routestatus/model/RouteStatus.java b/src/main/java/com/cta4j/alert/routestatus/model/RouteStatus.java index 73560215..4b7e566c 100644 --- a/src/main/java/com/cta4j/alert/routestatus/model/RouteStatus.java +++ b/src/main/java/com/cta4j/alert/routestatus/model/RouteStatus.java @@ -5,20 +5,17 @@ import java.net.URI; import java.util.Objects; -/** - * Represents the service status of a single route. - * - * @param route the name of this route (e.g., "Clark") - * @param color the color of this route used in maps; casing varies (e.g., "565a5c", "0065BD") - * @param textColor the suggested color of text displayed against {@code color}; casing varies (e.g., "ffffff", - * "FFFFFF") - * @param serviceId the unique GTFS route or station identifier of this route (e.g., "22") - * @param url the URL of this route's or station's page on transitchicago.com - * @param status the ultimate, human-readable status of this route (e.g., "Normal Service", "Service Change", - * "Bus Stop Note") - * @param statusColor the suggested color associated with {@code status}; length and casing vary (e.g., "000000", - * "06c", "B45F04") - */ +/// Represents the service status of a single route. +/// +/// @param route the name of this route (e.g., "Clark") +/// @param color the color of this route used in maps; casing varies (e.g., "565a5c", "0065BD") +/// @param textColor the suggested color of text displayed against `color`; casing varies (e.g., "ffffff", "FFFFFF") +/// @param serviceId the unique GTFS route or station identifier of this route (e.g., "22") +/// @param url the URL of this route's or station's page on transitchicago.com +/// @param status the ultimate, human-readable status of this route +/// (e.g., "Normal Service", "Service Change", "Bus Stop Note") +/// @param statusColor the suggested color associated with `status`; length and casing vary +/// (e.g., "000000", "06c", "B45F04") @NullMarked public record RouteStatus( String route, @@ -29,22 +26,20 @@ public record RouteStatus( String status, String statusColor ) { - /** - * Constructs a {@code RouteStatus}. - * - * @param route the name of the route (e.g., "Clark") - * @param color the color of the route used in maps; casing varies (e.g., "565a5c", "0065BD") - * @param textColor the suggested color of text displayed against {@code color}; casing varies (e.g., "ffffff", - * "FFFFFF") - * @param serviceId the unique GTFS route or station identifier of the route (e.g., "22") - * @param url the URL of the route's or station's page on transitchicago.com - * @param status the ultimate, human-readable status of the route (e.g., "Normal Service", "Service Change", - * "Bus Stop Note") - * @param statusColor the suggested color associated with {@code status}; length and casing vary (e.g., "000000", - * "06c", "B45F04") - * @throws NullPointerException if {@code route}, {@code color}, {@code textColor}, {@code serviceId}, - * {@code url}, {@code status}, or {@code statusColor} is {@code null} - */ + /// Constructs a `RouteStatus`. + /// + /// @param route the name of the route (e.g., "Clark") + /// @param color the color of the route used in maps; casing varies (e.g., "565a5c", "0065BD") + /// @param textColor the suggested color of text displayed against `color`; casing varies + /// (e.g., "ffffff", "FFFFFF") + /// @param serviceId the unique GTFS route or station identifier of the route (e.g., "22") + /// @param url the URL of the route's or station's page on transitchicago.com + /// @param status the ultimate, human-readable status of the route + /// (e.g., "Normal Service", "Service Change", "Bus Stop Note") + /// @param statusColor the suggested color associated with `status`; length and casing vary + /// (e.g., "000000", "06c", "B45F04") + /// @throws NullPointerException if `route`, `color`, `textColor`, `serviceId`, `url`, `status`, or `statusColor` + /// is `null` public RouteStatus { Objects.requireNonNull(route); Objects.requireNonNull(color); diff --git a/src/main/java/com/cta4j/bus/BusApi.java b/src/main/java/com/cta4j/bus/BusApi.java index f2330621..b8308bab 100644 --- a/src/main/java/com/cta4j/bus/BusApi.java +++ b/src/main/java/com/cta4j/bus/BusApi.java @@ -15,112 +15,83 @@ import java.time.Instant; import java.util.Objects; -/** - * Primary entry point for interacting with the CTA Bus Tracker API. - *

- * This interface provides access to the current system time as well as - * grouped sub-APIs for vehicles, routes, directions, stops, patterns, - * predictions, locales, and detours. - *

- * Instances of {@code BusApi} are immutable and thread-safe once built. - * Use {@link #builder(String)} to construct a configured instance. - */ +/// Primary entry point for interacting with the CTA Bus Tracker API. +/// +/// This interface provides access to the current system time as well as grouped sub-APIs for vehicles, routes, +/// directions, stops, patterns, predictions, locales, and detours. +/// +/// Instances of `BusApi` are immutable and thread-safe once built. Use [#builder(String)] to construct a configured +/// instance. @NullMarked public interface BusApi { - /** - * Returns the current system time reported by the Bus Tracker API. - * - * @return the API system time as an {@link Instant} - * @throws Cta4jException if the API returns an error response or the response cannot be parsed - */ + /// Returns the current system time reported by the Bus Tracker API. + /// + /// @return the API system time as an [Instant] + /// @throws Cta4jException if the API returns an error response or the response cannot be parsed Instant systemTime(); - /** - * Provides access to vehicle-related endpoints. - * - * @return the {@link VehiclesApi} - */ + /// Provides access to vehicle-related endpoints. + /// + /// @return the [VehiclesApi] VehiclesApi vehicles(); - /** - * Provides access to route-related endpoints. - * - * @return the {@link RoutesApi} - */ + /// Provides access to route-related endpoints. + /// + /// @return the [RoutesApi] RoutesApi routes(); - /** - * Provides access to direction-related endpoints. - * - * @return the {@link DirectionsApi} - */ + /// Provides access to direction-related endpoints. + /// + /// @return the [DirectionsApi] DirectionsApi directions(); - /** - * Provides access to stop-related endpoints. - * - * @return the {@link StopsApi} - */ + /// Provides access to stop-related endpoints. + /// + /// @return the [StopsApi] StopsApi stops(); - /** - * Provides access to route pattern–related endpoints. - * - * @return the {@link PatternsApi} - */ + /// Provides access to route pattern–related endpoints. + /// + /// @return the [PatternsApi] PatternsApi patterns(); - /** - * Provides access to prediction-related endpoints. - * - * @return the {@link PredictionsApi} - */ + /// Provides access to prediction-related endpoints. + /// + /// @return the [PredictionsApi] PredictionsApi predictions(); - /** - * Provides access to locale and language-related endpoints. - * - * @return the {@link LocalesApi} - */ + /// Provides access to locale and language-related endpoints. + /// + /// @return the [LocalesApi] LocalesApi locales(); - /** - * Provides access to detour-related endpoints. - * - * @return the {@link DetoursApi} - */ + /// Provides access to detour-related endpoints. + /// + /// @return the [DetoursApi] DetoursApi detours(); - /** - * Builder for constructing {@link BusApi} instances. - */ + /// Builder for constructing [BusApi] instances. interface Builder { - /** - * Sets the API host to use for requests. - *

- * If not specified, the default CTA Bus Tracker API host is used. - * - * @param host the API host - * @return this builder instance - * @throws NullPointerException if {@code host} is {@code null} - */ + /// Sets the API host to use for requests. + /// + /// If not specified, the default CTA Bus Tracker API host is used. + /// + /// @param host the API host + /// @return this builder instance + /// @throws NullPointerException if `host` is `null` Builder host(String host); - /** - * Builds a configured {@link BusApi} instance. - * - * @return a new {@link BusApi} - */ + /// Builds a configured [BusApi] instance. + /// + /// @return a new [BusApi] BusApi build(); } - /** - * Creates a new {@link Builder} for constructing a {@link BusApi}. - * - * @param apiKey the CTA Bus Tracker API key - * @return a new {@link Builder} - * @throws NullPointerException if {@code apiKey} is {@code null} - */ + /// Creates a new [Builder] for constructing a [BusApi]. + /// + /// @param apiKey the CTA Bus Tracker API key + /// @return a new [Builder] + /// @throws NullPointerException if `apiKey` is `null` static Builder builder(String apiKey) { Objects.requireNonNull(apiKey); diff --git a/src/main/java/com/cta4j/bus/common/exception/Cta4jBusException.java b/src/main/java/com/cta4j/bus/common/exception/Cta4jBusException.java index 742905b1..a3442ae3 100644 --- a/src/main/java/com/cta4j/bus/common/exception/Cta4jBusException.java +++ b/src/main/java/com/cta4j/bus/common/exception/Cta4jBusException.java @@ -7,52 +7,37 @@ import java.util.List; import java.util.Objects; -/** - * A custom exception class for handling cta4j bus-specific errors. - */ +/// A custom exception class for handling cta4j bus-specific errors. @NullMarked public final class Cta4jBusException extends Cta4jException { - /** - * Constructs a {@code Cta4jBusException}. - * - * @param message the detail message - * @param endpoint the endpoint associated with the exception - * @throws NullPointerException if {@code endpoint} is {@code null} - */ + /// Constructs a `Cta4jBusException`. + /// + /// @param message the detail message + /// @param endpoint the endpoint associated with the exception + /// @throws NullPointerException if `endpoint` is `null` public Cta4jBusException(String message, String endpoint) { super(message, endpoint); } - /** - * Constructs a {@code Cta4jBusException}. - * - * @param message the detail message - * @param endpoint the endpoint associated with the exception - * @param cause the cause of the exception - * @throws NullPointerException if {@code endpoint} is {@code null} - */ + /// Constructs a `Cta4jBusException`. + /// + /// @param message the detail message + /// @param endpoint the endpoint associated with the exception + /// @param cause the cause of the exception + /// @throws NullPointerException if `endpoint` is `null` public Cta4jBusException(String message, String endpoint, Throwable cause) { super(message, endpoint, cause); } - /** - * Constructs a {@code Cta4jBusException}. - * - * @param errors the list of {@link CtaError} objects - * @param endpoint the endpoint associated with the exception - * @throws NullPointerException if {@code errors} or {@code endpoint} is {@code null}, or if {@code errors} - * contains {@code null} elements - */ + /// Constructs a `Cta4jBusException`. + /// + /// @param errors the list of [CtaError] objects + /// @param endpoint the endpoint associated with the exception + /// @throws NullPointerException if `errors` or `endpoint` is `null`, or if `errors` contains `null` elements public Cta4jBusException(List errors, String endpoint) { super(joinMessages(errors), endpoint); } - /** - * Joins the messages from a list of {@link CtaError} objects into a single string. - * - * @param errors the list of {@link CtaError} objects - * @return a single string containing all error messages, separated by "; " - */ private static String joinMessages(List errors) { Objects.requireNonNull(errors); diff --git a/src/main/java/com/cta4j/bus/detour/DetoursApi.java b/src/main/java/com/cta4j/bus/detour/DetoursApi.java index 771dde95..488d3442 100644 --- a/src/main/java/com/cta4j/bus/detour/DetoursApi.java +++ b/src/main/java/com/cta4j/bus/detour/DetoursApi.java @@ -6,42 +6,33 @@ import java.util.List; -/** - * Provides access to detour-related endpoints of the CTA BusTime API. - *

- * This API allows retrieval of active service detours across all routes, - * or filtered by route and direction. - */ +/// Provides access to detour-related endpoints of the CTA BusTime API. +/// +/// This API allows retrieval of active service detours across all routes, or filtered by route and direction. @NullMarked public interface DetoursApi { - /** - * Retrieves all active detours. - * - * @return a {@link List} of active {@link Detour}s, or an empty {@link List} if no detours are found - * @throws Cta4jBusException if the API returns an error response or the response cannot be parsed - */ + /// Retrieves all active detours. + /// + /// @return a [List] of active [Detour]s, or an empty [List] if no detours are found + /// @throws Cta4jBusException if the API returns an error response or the response cannot be parsed List list(); - /** - * Retrieves all active detours for the specified route ID. - * - * @param routeId the route ID - * @return a {@link List} of {@link Detour}s associated with the route ID, or an empty {@link List} if no detours - * are found for the route ID - * @throws NullPointerException if {@code routeId} is {@code null} - * @throws Cta4jBusException if the API returns an error response or the response cannot be parsed - */ + /// Retrieves all active detours for the specified route ID. + /// + /// @param routeId the route ID + /// @return a [List] of [Detour]s associated with the route ID, or an empty [List] if no detours are found for the + /// route ID + /// @throws NullPointerException if `routeId` is `null` + /// @throws Cta4jBusException if the API returns an error response or the response cannot be parsed List findByRouteId(String routeId); - /** - * Retrieves all active detours for the specified route ID and direction. - * - * @param routeId the route ID - * @param direction the travel direction (e.g., "Northbound", "Southbound") - * @return a {@link List} of {@link Detour}s associated with the route ID and direction, or an empty {@link List} - * if no detours are found for the route ID and direction - * @throws NullPointerException if {@code routeId} or {@code direction} is {@code null} - * @throws Cta4jBusException if the API returns an error response or the response cannot be parsed - */ + /// Retrieves all active detours for the specified route ID and direction. + /// + /// @param routeId the route ID + /// @param direction the travel direction (e.g., "Northbound", "Southbound") + /// @return a [List] of [Detour]s associated with the route ID and direction, or an empty [List] if no detours are + /// found for the route ID and direction + /// @throws NullPointerException if `routeId` or `direction` is `null` + /// @throws Cta4jBusException if the API returns an error response or the response cannot be parsed List findByRouteIdAndDirection(String routeId, String direction); } diff --git a/src/main/java/com/cta4j/bus/detour/model/Detour.java b/src/main/java/com/cta4j/bus/detour/model/Detour.java index 080b34ed..a2cd487a 100644 --- a/src/main/java/com/cta4j/bus/detour/model/Detour.java +++ b/src/main/java/com/cta4j/bus/detour/model/Detour.java @@ -7,23 +7,19 @@ import java.util.List; import java.util.Objects; -/** - * Represents a service detour affecting one or more routes and directions within a specific time window. - * - *

- * NOTE: {@code dataFeed} is not well-documented by the CTA. As such, its presence here is primarily for - * completeness and may not be populated or described correctly. - *

- * - * @param id the unique identifier of this detour - * @param version the version of this detour - * @param active whether this detour is currently active - * @param description the human-readable description of this detour - * @param routeDirections the routes and directions affected by this detour - * @param startTime the time at which this detour begins - * @param endTime the time at which this detour ends - * @param dataFeed the identifier for the data feed that supplied this detour, or {@code null} if not available - */ +/// Represents a service detour affecting one or more routes and directions within a specific time window. +/// +/// **NOTE:** `dataFeed` is not well-documented by the CTA. As such, its presence here is primarily for completeness +/// and may not be populated or described correctly. +/// +/// @param id the unique identifier of this detour +/// @param version the version of this detour +/// @param active whether this detour is currently active +/// @param description the human-readable description of this detour +/// @param routeDirections the routes and directions affected by this detour +/// @param startTime the time at which this detour begins +/// @param endTime the time at which this detour ends +/// @param dataFeed the identifier for the data feed that supplied this detour, or `null` if not available @NullMarked public record Detour( String id, @@ -35,21 +31,18 @@ public record Detour( Instant endTime, @Nullable String dataFeed ) { - /** - * Constructs a {@code Detour}. - * - * @param id the unique identifier of the detour - * @param version the version of the detour - * @param active whether the detour is currently active - * @param description the human-readable description of the detour - * @param routeDirections the routes and directions affected by the detour - * @param startTime the time at which the detour begins - * @param endTime the time at which the detour ends - * @param dataFeed the identifier for the data feed that supplied the detour, or {@code null} if not available - * @throws NullPointerException if {@code id}, {@code version}, {@code description}, {@code routeDirections}, - * {@code startTime}, or {@code endTime} is {@code null}, or if any element of {@code routeDirections} is - * {@code null} - */ + /// Constructs a `Detour`. + /// + /// @param id the unique identifier of the detour + /// @param version the version of the detour + /// @param active whether the detour is currently active + /// @param description the human-readable description of the detour + /// @param routeDirections the routes and directions affected by the detour + /// @param startTime the time at which the detour begins + /// @param endTime the time at which the detour ends + /// @param dataFeed the identifier for the data feed that supplied the detour, or `null` if not available + /// @throws NullPointerException if `id`, `version`, `description`, `routeDirections`, `startTime`, or `endTime` is + /// `null`, or if any element of `routeDirections` is `null` public Detour { Objects.requireNonNull(id); Objects.requireNonNull(version); diff --git a/src/main/java/com/cta4j/bus/detour/model/DetourRouteDirection.java b/src/main/java/com/cta4j/bus/detour/model/DetourRouteDirection.java index 74b36915..729af5ae 100644 --- a/src/main/java/com/cta4j/bus/detour/model/DetourRouteDirection.java +++ b/src/main/java/com/cta4j/bus/detour/model/DetourRouteDirection.java @@ -4,24 +4,20 @@ import java.util.Objects; -/** - * Represents a route and direction affected by a detour. - * - * @param routeId the route ID of this detour - * @param direction the direction of this detour (e.g., "Northbound", "Southbound") - */ +/// Represents a route and direction affected by a detour. +/// +/// @param routeId the route ID of this detour +/// @param direction the direction of this detour (e.g., "Northbound", "Southbound") @NullMarked public record DetourRouteDirection( String routeId, String direction ) { - /** - * Constructs a {@code DetourRouteDirection}. - * - * @param routeId the route ID of the detour - * @param direction the direction of the detour (e.g., "Northbound", "Southbound") - * @throws NullPointerException if {@code routeId} or {@code direction} is {@code null} - */ + /// Constructs a `DetourRouteDirection`. + /// + /// @param routeId the route ID of the detour + /// @param direction the direction of the detour (e.g., "Northbound", "Southbound") + /// @throws NullPointerException if `routeId` or `direction` is `null` public DetourRouteDirection { Objects.requireNonNull(routeId); Objects.requireNonNull(direction); diff --git a/src/main/java/com/cta4j/bus/direction/DirectionsApi.java b/src/main/java/com/cta4j/bus/direction/DirectionsApi.java index 575edd72..33a312b3 100644 --- a/src/main/java/com/cta4j/bus/direction/DirectionsApi.java +++ b/src/main/java/com/cta4j/bus/direction/DirectionsApi.java @@ -5,21 +5,17 @@ import java.util.List; -/** - * Provides access to direction-related endpoints of the CTA BusTime API. - *

- * This API allows retrieval of available travel directions for a given route. - */ +/// Provides access to direction-related endpoints of the CTA BusTime API. +/// +/// This API allows retrieval of available travel directions for a given route. @NullMarked public interface DirectionsApi { - /** - * Retrieves the available travel directions for the specified route (e.g., "Northbound", "Southbound"). - * - * @param routeId the route identifier - * @return a {@link List} of direction identifiers for the route, or an empty {@link List} if no directions are - * found for the route - * @throws NullPointerException if {@code routeId} is {@code null} - * @throws Cta4jBusException if the API returns an error response or the response cannot be parsed - */ + /// Retrieves the available travel directions for the specified route (e.g., "Northbound", "Southbound"). + /// + /// @param routeId the route identifier + /// @return a [List] of direction identifiers for the route, or an empty [List] if no directions are found for the + /// route + /// @throws NullPointerException if `routeId` is `null` + /// @throws Cta4jBusException if the API returns an error response or the response cannot be parsed List findByRouteId(String routeId); } diff --git a/src/main/java/com/cta4j/bus/locale/LocalesApi.java b/src/main/java/com/cta4j/bus/locale/LocalesApi.java index b329c5cf..97b6ee1c 100644 --- a/src/main/java/com/cta4j/bus/locale/LocalesApi.java +++ b/src/main/java/com/cta4j/bus/locale/LocalesApi.java @@ -7,39 +7,30 @@ import java.util.List; import java.util.Locale; -/** - * Provides access to locale-related endpoints of the CTA BusTime API. - *

- * This API allows retrieval of supported locales for the CTA BusTime services. - */ +/// Provides access to locale-related endpoints of the CTA BusTime API. +/// +/// This API allows retrieval of supported locales for the CTA BusTime services. @NullMarked public interface LocalesApi { - /** - * Retrieves the supported locales. - * - * @return a {@link List} of {@link SupportedLocale}s, or an empty {@link List} if no supported locales are - * found - * @throws Cta4jBusException if the API returns an error response or the response cannot be parsed - */ + /// Retrieves the supported locales. + /// + /// @return a [List] of [SupportedLocale]s, or an empty [List] if no supported locales are found + /// @throws Cta4jBusException if the API returns an error response or the response cannot be parsed List list(); - /** - * Retrieves the supported locales, with names displayed in the specified locale. - * - * @param displayLocale the locale in which to display the names of the supported locales - * @return a {@link List} of {@link SupportedLocale}s with names in the specified locale, or an empty {@link List} - * if no supported locales are found - * @throws NullPointerException if {@code displayLocale} is {@code null} - * @throws Cta4jBusException if the API returns an error response or the response cannot be parsed - */ + /// Retrieves the supported locales, with names displayed in the specified locale. + /// + /// @param displayLocale the locale in which to display the names of the supported locales + /// @return a [List] of [SupportedLocale]s with names in the specified locale, or an empty [List] if no supported + /// locales are found + /// @throws NullPointerException if `displayLocale` is `null` + /// @throws Cta4jBusException if the API returns an error response or the response cannot be parsed List list(Locale displayLocale); - /** - * Retrieves the supported locales, with names displayed in their native languages. - * - * @return a {@link List} of {@link SupportedLocale}s with names in their native languages, or an empty - * {@link List} if no supported locales are found - * @throws Cta4jBusException if the API returns an error response or the response cannot be parsed - */ + /// Retrieves the supported locales, with names displayed in their native languages. + /// + /// @return a [List] of [SupportedLocale]s with names in their native languages, or an empty [List] if no supported + /// locales are found + /// @throws Cta4jBusException if the API returns an error response or the response cannot be parsed List listInNativeLanguage(); } diff --git a/src/main/java/com/cta4j/bus/locale/model/SupportedLocale.java b/src/main/java/com/cta4j/bus/locale/model/SupportedLocale.java index 59ba0f71..9b8d1afb 100644 --- a/src/main/java/com/cta4j/bus/locale/model/SupportedLocale.java +++ b/src/main/java/com/cta4j/bus/locale/model/SupportedLocale.java @@ -5,24 +5,20 @@ import java.util.Locale; import java.util.Objects; -/** - * Represents a locale supported by the CTA Bus API. - * - * @param locale the supported {@link Locale} - * @param displayName the human-readable name of this supported locale (e.g., "English", "Spanish") - */ +/// Represents a locale supported by the CTA Bus API. +/// +/// @param locale the supported [Locale] +/// @param displayName the human-readable name of this supported locale (e.g., "English", "Spanish") @NullMarked public record SupportedLocale( Locale locale, String displayName ) { - /** - * Constructs a {@code SupportedLocale}. - * - * @param locale the supported {@link Locale} - * @param displayName the human-readable name of the supported locale (e.g., "English", "Spanish") - * @throws NullPointerException if {@code locale} or {@code displayName} is {@code null} - */ + /// Constructs a `SupportedLocale`. + /// + /// @param locale the supported [Locale] + /// @param displayName the human-readable name of the supported locale (e.g., "English", "Spanish") + /// @throws NullPointerException if `locale` or `displayName` is `null` public SupportedLocale { Objects.requireNonNull(locale); Objects.requireNonNull(displayName); diff --git a/src/main/java/com/cta4j/bus/pattern/PatternsApi.java b/src/main/java/com/cta4j/bus/pattern/PatternsApi.java index 0ba804a2..6944ce87 100644 --- a/src/main/java/com/cta4j/bus/pattern/PatternsApi.java +++ b/src/main/java/com/cta4j/bus/pattern/PatternsApi.java @@ -10,35 +10,29 @@ import java.util.Objects; import java.util.Optional; -/** - * Provides access to route pattern-related endpoints of the CTA BusTime API. - *

- * This API allows retrieval of route patterns by their IDs or by associated route IDs. - */ +/// Provides access to route pattern-related endpoints of the CTA BusTime API. +/// +/// This API allows retrieval of route patterns by their IDs or by associated route IDs. @NullMarked public interface PatternsApi { - /** - * Retrieves route patterns by their pattern IDs. - * - * @param patternIds a {@link Collection} of route pattern IDs - * @return a {@link List} of {@link RoutePattern}s corresponding to the provided IDs, or an empty {@link List} if - * no patterns are found - * @throws NullPointerException if {@code patternIds} is {@code null} or contains {@code null} elements - * @throws IllegalArgumentException if more than 10 pattern IDs are provided - * @throws Cta4jBusException if the API returns an error response or the response cannot be parsed - */ + /// Retrieves route patterns by their pattern IDs. + /// + /// @param patternIds a [Collection] of route pattern IDs + /// @return a [List] of [RoutePattern]s corresponding to the provided IDs, or an empty [List] if no patterns are + /// found + /// @throws NullPointerException if `patternIds` is `null` or contains `null` elements + /// @throws IllegalArgumentException if more than 10 pattern IDs are provided + /// @throws Cta4jBusException if the API returns an error response or the response cannot be parsed List findByIds(Collection patternIds); - /** - * Retrieves a route pattern by its pattern ID. - * - * @param patternId the route pattern ID - * @return an {@link Optional} containing the {@link RoutePattern} if found, or an empty {@link Optional} if no - * pattern is found for the given ID - * @throws NullPointerException if {@code patternId} is {@code null} - * @throws Cta4jBusException if multiple route patterns are found for the given ID, or if the API returns an error - * response or the response cannot be parsed - */ + /// Retrieves a route pattern by its pattern ID. + /// + /// @param patternId the route pattern ID + /// @return an [Optional] containing the [RoutePattern] if found, or an empty [Optional] if no pattern is found for + /// the given ID + /// @throws NullPointerException if `patternId` is `null` + /// @throws Cta4jBusException if multiple route patterns are found for the given ID, or if the API returns an error + /// response or the response cannot be parsed default Optional findById(String patternId) { Objects.requireNonNull(patternId); @@ -61,14 +55,12 @@ default Optional findById(String patternId) { return Optional.of(pattern); } - /** - * Retrieves all route patterns for the specified route ID. - * - * @param routeId the route ID - * @return a {@link List} of {@link RoutePattern}s associated with the route ID, or an empty {@link List} if no - * patterns are found for the route ID - * @throws NullPointerException if {@code routeId} is {@code null} - * @throws Cta4jBusException if the API returns an error response or the response cannot be parsed - */ + /// Retrieves all route patterns for the specified route ID. + /// + /// @param routeId the route ID + /// @return a [List] of [RoutePattern]s associated with the route ID, or an empty [List] if no patterns are found + /// for the route ID + /// @throws NullPointerException if `routeId` is `null` + /// @throws Cta4jBusException if the API returns an error response or the response cannot be parsed List findByRouteId(String routeId); } diff --git a/src/main/java/com/cta4j/bus/pattern/model/PatternPoint.java b/src/main/java/com/cta4j/bus/pattern/model/PatternPoint.java index 0023b1d9..9101c6ba 100644 --- a/src/main/java/com/cta4j/bus/pattern/model/PatternPoint.java +++ b/src/main/java/com/cta4j/bus/pattern/model/PatternPoint.java @@ -6,17 +6,15 @@ import java.math.BigDecimal; import java.util.Objects; -/** - * Represents a point in a bus route pattern. - * - * @param sequence the position of this pattern point in the overall sequence of points - * @param type the type of this pattern point - * @param stopId the identifier of the stop associated with this pattern point, if applicable - * @param stopName the name of the stop associated with this pattern point, if applicable - * @param distanceToPatternPoint the distance from this pattern point to the next, if applicable - * @param latitude the latitude coordinate of this pattern point - * @param longitude the longitude coordinate of this pattern point - */ +/// Represents a point in a bus route pattern. +/// +/// @param sequence the position of this pattern point in the overall sequence of points +/// @param type the type of this pattern point +/// @param stopId the identifier of the stop associated with this pattern point, if applicable +/// @param stopName the name of the stop associated with this pattern point, if applicable +/// @param distanceToPatternPoint the distance from this pattern point to the next, if applicable +/// @param latitude the latitude coordinate of this pattern point +/// @param longitude the longitude coordinate of this pattern point @NullMarked public record PatternPoint( int sequence, @@ -27,18 +25,16 @@ public record PatternPoint( BigDecimal latitude, BigDecimal longitude ) { - /** - * Constructs a {@code PatternPoint}. - * - * @param sequence the position of the pattern point in the overall sequence of points - * @param type the type of the pattern point - * @param stopId the identifier of the stop associated with the pattern point, if applicable - * @param stopName the name of the stop associated with the pattern point, if applicable - * @param distanceToPatternPoint the distance from the pattern point to the next, if applicable - * @param latitude the latitude coordinate of the pattern point - * @param longitude the longitude coordinate of the pattern point - * @throws NullPointerException if {@code type}, {@code latitude}, or {@code longitude} is {@code null} - */ + /// Constructs a `PatternPoint`. + /// + /// @param sequence the position of the pattern point in the overall sequence of points + /// @param type the type of the pattern point + /// @param stopId the identifier of the stop associated with the pattern point, if applicable + /// @param stopName the name of the stop associated with the pattern point, if applicable + /// @param distanceToPatternPoint the distance from the pattern point to the next, if applicable + /// @param latitude the latitude coordinate of the pattern point + /// @param longitude the longitude coordinate of the pattern point + /// @throws NullPointerException if `type`, `latitude`, or `longitude` is `null` public PatternPoint { Objects.requireNonNull(type); Objects.requireNonNull(latitude); diff --git a/src/main/java/com/cta4j/bus/pattern/model/PatternPointType.java b/src/main/java/com/cta4j/bus/pattern/model/PatternPointType.java index e18c5f5d..4b0dec9c 100644 --- a/src/main/java/com/cta4j/bus/pattern/model/PatternPointType.java +++ b/src/main/java/com/cta4j/bus/pattern/model/PatternPointType.java @@ -2,18 +2,12 @@ import org.jspecify.annotations.NullMarked; -/** - * Represents the type of point within a route or pattern geometry. - */ +/// Represents the type of point within a route or pattern geometry. @NullMarked public enum PatternPointType { - /** - * Indicates a stop along the route. - */ + /// Indicates a stop along the route. STOP, - /** - * Indicates a waypoint along the route. - */ + /// Indicates a waypoint along the route. WAYPOINT } diff --git a/src/main/java/com/cta4j/bus/pattern/model/RoutePattern.java b/src/main/java/com/cta4j/bus/pattern/model/RoutePattern.java index 542d70aa..8b64d77c 100644 --- a/src/main/java/com/cta4j/bus/pattern/model/RoutePattern.java +++ b/src/main/java/com/cta4j/bus/pattern/model/RoutePattern.java @@ -6,17 +6,14 @@ import java.util.List; import java.util.Objects; -/** - * Represents a bus route pattern. - * - * @param id the unique identifier of this route pattern - * @param length the length of this route pattern in feet - * @param direction the direction of this route pattern (e.g., "Northbound", "Southbound") - * @param points the {@link List} of pattern points that make up this route pattern - * @param detourId the identifier of the detour associated with this route pattern, if applicable - * @param detourPoints the {@link List} of pattern points of the detour associated with this route pattern, if - * applicable - */ +/// Represents a bus route pattern. +/// +/// @param id the unique identifier of this route pattern +/// @param length the length of this route pattern in feet +/// @param direction the direction of this route pattern (e.g., "Northbound", "Southbound") +/// @param points the [List] of pattern points that make up this route pattern +/// @param detourId the identifier of the detour associated with this route pattern, if applicable +/// @param detourPoints the [List] of pattern points of the detour associated with this route pattern, if applicable @NullMarked public record RoutePattern( String id, @@ -26,19 +23,16 @@ public record RoutePattern( @Nullable String detourId, @Nullable List detourPoints ) { - /** - * Constructs a {@code RoutePattern}. - * - * @param id the unique identifier of the route pattern - * @param length the length of the route pattern in feet - * @param direction the direction of the route pattern (e.g., "Northbound", "Southbound") - * @param points the {@link List} of pattern points that make up the route pattern - * @param detourId the identifier of the detour associated with the route pattern, if applicable - * @param detourPoints the {@link List} of pattern points of the detour associated with the route pattern, if - * applicable - * @throws NullPointerException if {@code id}, {@code direction}, or {@code points} is {@code null}, or if any - * element of {@code points} or {@code detourPoints} is {@code null} - */ + /// Constructs a `RoutePattern`. + /// + /// @param id the unique identifier of the route pattern + /// @param length the length of the route pattern in feet + /// @param direction the direction of the route pattern (e.g., "Northbound", "Southbound") + /// @param points the [List] of pattern points that make up the route pattern + /// @param detourId the identifier of the detour associated with the route pattern, if applicable + /// @param detourPoints the [List] of pattern points of the detour associated with the route pattern, if applicable + /// @throws NullPointerException if `id`, `direction`, or `points` is `null`, or if any element of `points` or + /// `detourPoints` is `null` public RoutePattern { Objects.requireNonNull(id); Objects.requireNonNull(direction); diff --git a/src/main/java/com/cta4j/bus/prediction/PredictionsApi.java b/src/main/java/com/cta4j/bus/prediction/PredictionsApi.java index a8f8abc9..a62018ff 100644 --- a/src/main/java/com/cta4j/bus/prediction/PredictionsApi.java +++ b/src/main/java/com/cta4j/bus/prediction/PredictionsApi.java @@ -10,34 +10,27 @@ import java.util.List; import java.util.Objects; -/** - * Provides access to prediction-related endpoints of the CTA BusTime API. - *

- * This API allows retrieval of predictions by stop IDs or vehicle IDs. - */ +/// Provides access to prediction-related endpoints of the CTA BusTime API. +/// +/// This API allows retrieval of predictions by stop IDs or vehicle IDs. @NullMarked public interface PredictionsApi { - /** - * Retrieves predictions by stop IDs. - * - * @param query the query parameters for fetching predictions by stop IDs - * @return a {@link List} of {@link Prediction}s corresponding to the provided stop IDs, or an empty {@link List} - * if no predictions are found - * @throws NullPointerException if {@code query} is {@code null} - * @throws Cta4jBusException if the API returns an error response or the response cannot be parsed - */ + /// Retrieves predictions by stop IDs. + /// + /// @param query the query parameters for fetching predictions by stop IDs + /// @return a [List] of [Prediction]s corresponding to the provided stop IDs, or an empty [List] if no predictions + /// are found + /// @throws NullPointerException if `query` is `null` + /// @throws Cta4jBusException if the API returns an error response or the response cannot be parsed List findByStopIds(StopPredictionsQuery query); - /** - * Retrieves predictions by stop IDs. - * - * @param stopIds a {@link Collection} of stop IDs - * @return a {@link List} of {@link Prediction}s corresponding to the provided stop IDs, or an empty {@link List} - * if no predictions are found - * @throws NullPointerException if {@code stopIds} is {@code null}, or if any element of {@code stopIds} is - * {@code null} - * @throws Cta4jBusException if the API returns an error response or the response cannot be parsed - */ + /// Retrieves predictions by stop IDs. + /// + /// @param stopIds a [Collection] of stop IDs + /// @return a [List] of [Prediction]s corresponding to the provided stop IDs, or an empty [List] if no predictions + /// are found + /// @throws NullPointerException if `stopIds` is `null`, or if any element of `stopIds` is `null` + /// @throws Cta4jBusException if the API returns an error response or the response cannot be parsed default List findByStopIds(Collection stopIds) { Objects.requireNonNull(stopIds); @@ -49,15 +42,13 @@ default List findByStopIds(Collection stopIds) { return this.findByStopIds(query); } - /** - * Retrieves predictions by stop ID. - * - * @param stopId the stop ID - * @return a {@link List} of {@link Prediction}s corresponding to the provided stop ID, or an empty {@link List} if - * no predictions are found - * @throws NullPointerException if {@code stopId} is {@code null} - * @throws Cta4jBusException if the API returns an error response or the response cannot be parsed - */ + /// Retrieves predictions by stop ID. + /// + /// @param stopId the stop ID + /// @return a [List] of [Prediction]s corresponding to the provided stop ID, or an empty [List] if no predictions + /// are found + /// @throws NullPointerException if `stopId` is `null` + /// @throws Cta4jBusException if the API returns an error response or the response cannot be parsed default List findByStopId(String stopId) { Objects.requireNonNull(stopId); @@ -69,27 +60,22 @@ default List findByStopId(String stopId) { return this.findByStopIds(query); } - /** - * Retrieves predictions by vehicle IDs. - * - * @param query the query parameters for fetching predictions by vehicle IDs - * @return a {@link List} of {@link Prediction}s corresponding to the provided vehicle IDs, or an empty - * {@link List} if no predictions are found - * @throws NullPointerException if {@code query} is {@code null} - * @throws Cta4jBusException if the API returns an error response or the response cannot be parsed - */ + /// Retrieves predictions by vehicle IDs. + /// + /// @param query the query parameters for fetching predictions by vehicle IDs + /// @return a [List] of [Prediction]s corresponding to the provided vehicle IDs, or an empty [List] if no + /// predictions are found + /// @throws NullPointerException if `query` is `null` + /// @throws Cta4jBusException if the API returns an error response or the response cannot be parsed List findByVehicleIds(VehiclePredictionsQuery query); - /** - * Retrieves predictions by vehicle IDs. - * - * @param vehicleIds a {@link Collection} of vehicle IDs - * @return a {@link List} of {@link Prediction}s corresponding to the provided vehicle IDs, or an empty - * {@link List} if no predictions are found - * @throws NullPointerException if {@code vehicleIds} is {@code null}, or if any element of {@code vehicleIds} - * is {@code null} - * @throws Cta4jBusException if the API returns an error response or the response cannot be parsed - */ + /// Retrieves predictions by vehicle IDs. + /// + /// @param vehicleIds a [Collection] of vehicle IDs + /// @return a [List] of [Prediction]s corresponding to the provided vehicle IDs, or an empty [List] if no + /// predictions are found + /// @throws NullPointerException if `vehicleIds` is `null`, or if any element of `vehicleIds` is `null` + /// @throws Cta4jBusException if the API returns an error response or the response cannot be parsed default List findByVehicleIds(Collection vehicleIds) { Objects.requireNonNull(vehicleIds); @@ -101,15 +87,13 @@ default List findByVehicleIds(Collection vehicleIds) { return this.findByVehicleIds(query); } - /** - * Retrieves predictions by vehicle ID. - * - * @param vehicleId the vehicle ID - * @return a {@link List} of {@link Prediction}s corresponding to the provided vehicle ID, or an empty {@link List} - * if no predictions are found - * @throws NullPointerException if {@code vehicleId} is {@code null} - * @throws Cta4jBusException if the API returns an error response or the response cannot be parsed - */ + /// Retrieves predictions by vehicle ID. + /// + /// @param vehicleId the vehicle ID + /// @return a [List] of [Prediction]s corresponding to the provided vehicle ID, or an empty [List] if no + /// predictions are found + /// @throws NullPointerException if `vehicleId` is `null` + /// @throws Cta4jBusException if the API returns an error response or the response cannot be parsed default List findByVehicleId(String vehicleId) { Objects.requireNonNull(vehicleId); @@ -121,16 +105,14 @@ default List findByVehicleId(String vehicleId) { return this.findByVehicleIds(query); } - /** - * Retrieves predictions by route ID and stop ID. - * - * @param routeId the route ID - * @param stopId the stop ID - * @return a {@link List} of {@link Prediction}s corresponding to the provided route ID and stop ID, or an empty - * {@link List} if no predictions are found - * @throws NullPointerException if {@code routeId} or {@code stopId} is {@code null} - * @throws Cta4jBusException if the API returns an error response or the response cannot be parsed - */ + /// Retrieves predictions by route ID and stop ID. + /// + /// @param routeId the route ID + /// @param stopId the stop ID + /// @return a [List] of [Prediction]s corresponding to the provided route ID and stop ID, or an empty [List] if no + /// predictions are found + /// @throws NullPointerException if `routeId` or `stopId` is `null` + /// @throws Cta4jBusException if the API returns an error response or the response cannot be parsed default List findByRouteIdAndStopId(String routeId, String stopId) { Objects.requireNonNull(routeId); Objects.requireNonNull(stopId); diff --git a/src/main/java/com/cta4j/bus/prediction/model/DynamicAction.java b/src/main/java/com/cta4j/bus/prediction/model/DynamicAction.java index 269ed677..743b912d 100644 --- a/src/main/java/com/cta4j/bus/prediction/model/DynamicAction.java +++ b/src/main/java/com/cta4j/bus/prediction/model/DynamicAction.java @@ -2,107 +2,65 @@ import org.jspecify.annotations.NullMarked; -/** - * Represents the various dynamic actions that can be applied to a bus trip. - */ +/// Represents the various dynamic actions that can be applied to a bus trip. @NullMarked public enum DynamicAction { - /** - * Indicates that no dynamic action has been applied. - */ + /// Indicates that no dynamic action has been applied. NONE(0), - /** - * Indicates that the event or trip has been canceled. - */ + /// Indicates that the event or trip has been canceled. CANCELLED(1), - /** - * Indicates that the event or trip will be handled by a different vehicle or operator. - */ + /// Indicates that the event or trip will be handled by a different vehicle or operator. REASSIGNED(2), - /** - * Indicates that the time of the event, or the entire trip, has been moved. - */ + /// Indicates that the time of the event, or the entire trip, has been moved. SHIFTED(3), - /** - * Indicates that the event is “drop-off only” and will not stop to pick up passengers. - */ + /// Indicates that the event is “drop-off only” and will not stop to pick up passengers. EXPRESSED(4), - /** - * Indicates that the trip has events that are affected by Disruption Management changes, but the trip itself is - * not affected. - */ + /// Indicates that the trip has events that are affected by Disruption Management changes, but the trip itself is + /// not affected. STOPS_AFFECTED(6), - /** - * Indicates that the trip was created dynamically and does not appear in the TA schedule. - */ + /// Indicates that the trip was created dynamically and does not appear in the TA schedule. NEW_TRIP(8), - /** - * Indicates one of the following: - *

    - *
  • - * The trip has been split, and this part of the split is using the original trip identifier(s). - *
  • - *
  • - * The trip has been short-turned leading to the removal of short-turned stops from the trip resulting in - * the trip being partial. - *
  • - *
- */ + /// Indicates one of the following: + /// - The trip has been split, and this part of the split is using the original trip identifier(s). + /// - The trip has been short-turned leading to the removal of short-turned stops from the trip resulting in the + /// trip being partial. PARTIAL_TRIP(9), - /** - * Indicates the trip has been split, and this part of the split has been assigned a new trip identifier(s). - */ + /// Indicates the trip has been split, and this part of the split has been assigned a new trip identifier(s). PARTIAL_TRIP_NEW(10), - /** - * Indicates that the event or trip has been marked as canceled, but the cancellation should not be shown to the - * public. - */ + /// Indicates that the event or trip has been marked as canceled, but the cancellation should not be shown to the + /// public. DELAYED_CANCEL(12), - /** - * Indicates that event has been added to the trip. It was not originally scheduled. - */ + /// Indicates that event has been added to the trip. It was not originally scheduled. ADDED_STOP(13), - /** - * Indicates that the trip has been affected by a delay. - */ + /// Indicates that the trip has been affected by a delay. UNKNOWN_DELAY(14), - /** - * Indicates that the trip, which was created dynamically, has been affected by a delay. - */ + /// Indicates that the trip, which was created dynamically, has been affected by a delay. UNKNOWN_DELAY_NEW(15), - /** - * Indicates that the trip has been invalidated. Predictions for it should not be shown to the public. - */ + /// Indicates that the trip has been invalidated. Predictions for it should not be shown to the public. INVALIDATED_TRIP(16), - /** - * Indicates that the trip, which was created dynamically, has been invalidated. Predictions for it should not be - * shown to the public. - */ + /// Indicates that the trip, which was created dynamically, has been invalidated. Predictions for it should not be + /// shown to the public. INVALIDATED_TRIP_NEW(17), - /** - * Indicates that the trip, which was created dynamically, has been canceled. - */ + /// Indicates that the trip, which was created dynamically, has been canceled. CANCELLED_TRIP_NEW(18), - /** - * Indicates that the trip, which was created dynamically, has events that are affected by Disruption Management - * changes, but the trip itself is not affected. - */ + /// Indicates that the trip, which was created dynamically, has events that are affected by Disruption Management + /// changes, but the trip itself is not affected. STOPS_AFFECTED_NEW(19); private final int code; @@ -111,22 +69,18 @@ public enum DynamicAction { this.code = code; } - /** - * Gets the code associated with this dynamic action. - * - * @return the dynamic action code - */ + /// Gets the code associated with this dynamic action. + /// + /// @return the dynamic action code public int getCode() { return this.code; } - /** - * Returns the {@code DynamicAction} corresponding to the given code. - * - * @param code the dynamic action code - * @return the corresponding {@code DynamicAction} - * @throws IllegalArgumentException if the code does not correspond to any known dynamic action - */ + /// Returns the `DynamicAction` corresponding to the given code. + /// + /// @param code the dynamic action code + /// @return the corresponding `DynamicAction` + /// @throws IllegalArgumentException if the code does not correspond to any known dynamic action public static DynamicAction fromCode(int code) { return switch (code) { case 0 -> NONE; diff --git a/src/main/java/com/cta4j/bus/prediction/model/FlagStop.java b/src/main/java/com/cta4j/bus/prediction/model/FlagStop.java index 53683d50..0eeb3748 100644 --- a/src/main/java/com/cta4j/bus/prediction/model/FlagStop.java +++ b/src/main/java/com/cta4j/bus/prediction/model/FlagStop.java @@ -2,29 +2,19 @@ import org.jspecify.annotations.NullMarked; -/** - * Represents the flag-stop information for a prediction. - */ +/// Represents the flag-stop information for a prediction. @NullMarked public enum FlagStop { - /** - * Indicates that no flag-stop information is available. - */ + /// Indicates that no flag-stop information is available. UNDEFINED(-1), - /** - * Indicates a normal stop. - */ + /// Indicates a normal stop. NORMAL(0), - /** - * Indicates a stop where passengers are both picked up and discharged. - */ + /// Indicates a stop where passengers are both picked up and discharged. PICKUP_AND_DISCHARGE(1), - /** - * Indicates a stop where only discharging of passengers occurs. - */ + /// Indicates a stop where only discharging of passengers occurs. ONLY_DISCHARGE(2); private final int code; @@ -33,22 +23,18 @@ public enum FlagStop { this.code = code; } - /** - * Gets the code associated with this flag-stop. - * - * @return the flag-stop code - */ + /// Gets the code associated with this flag-stop. + /// + /// @return the flag-stop code public int getCode() { return this.code; } - /** - * Returns the {@code FlagStop} corresponding to the given code. - * - * @param code the flag-stop code - * @return the corresponding {@code FlagStop} - * @throws IllegalArgumentException if the code does not correspond to any known flag-stop - */ + /// Returns the `FlagStop` corresponding to the given code. + /// + /// @param code the flag-stop code + /// @return the corresponding `FlagStop` + /// @throws IllegalArgumentException if the code does not correspond to any known flag-stop public static FlagStop fromCode(int code) { return switch (code) { case -1 -> UNDEFINED; diff --git a/src/main/java/com/cta4j/bus/prediction/model/PassengerLoad.java b/src/main/java/com/cta4j/bus/prediction/model/PassengerLoad.java index 06e2013f..4bede640 100644 --- a/src/main/java/com/cta4j/bus/prediction/model/PassengerLoad.java +++ b/src/main/java/com/cta4j/bus/prediction/model/PassengerLoad.java @@ -2,28 +2,18 @@ import org.jspecify.annotations.NullMarked; -/** - * Represents the passenger load information for a prediction. - */ +/// Represents the passenger load information for a prediction. @NullMarked public enum PassengerLoad { - /** - * Indicates that the bus is full. - */ + /// Indicates that the bus is full. FULL, - /** - * Indicates that the bus is half full. - */ + /// Indicates that the bus is half full. HALF_EMPTY, - /** - * Indicates that the bus is empty. - */ + /// Indicates that the bus is empty. EMPTY, - /** - * Indicates that no passenger load information is available. - */ + /// Indicates that no passenger load information is available. UNKNOWN } diff --git a/src/main/java/com/cta4j/bus/prediction/model/Prediction.java b/src/main/java/com/cta4j/bus/prediction/model/Prediction.java index ad69adbc..b91af267 100644 --- a/src/main/java/com/cta4j/bus/prediction/model/Prediction.java +++ b/src/main/java/com/cta4j/bus/prediction/model/Prediction.java @@ -8,25 +8,24 @@ import java.time.Instant; import java.util.Objects; -/** - * Represents a bus arrival prediction. - * - * @param predictionType the type of this prediction - * @param stopId the unique identifier of the stop for which this prediction was generated - * @param stopName the display name of the stop for which this prediction was generated - * @param vehicleId the unique identifier of the vehicle for which this prediction was generated - * @param distanceToStop the feet left to be traveled by the vehicle before it reaches the stop associated with this - * prediction - * @param routeId the alphanumeric designator of the route (e.g., "20" or "X9") for which this prediction was generated - * @param routeDesignator the language-specific route designator of this prediction, intended for display; identical - * to {@code routeId} in practice (e.g., "20") - * @param routeDirection the direction of travel of the route associated with this prediction (e.g., "Eastbound") - * @param destination the final destination of the vehicle associated with this prediction - * @param arrivalTime the predicted date and time (UTC) of a vehicle’s arrival or departure to the stop associated with - * this prediction - * @param delayed whether the vehicle associated with this prediction is currently delayed - * @param metadata the metadata associated with this prediction - */ +/// Represents a bus arrival prediction. +/// +/// @param predictionType the type of this prediction +/// @param stopId the unique identifier of the stop for which this prediction was generated +/// @param stopName the display name of the stop for which this prediction was generated +/// @param vehicleId the unique identifier of the vehicle for which this prediction was generated +/// @param distanceToStop the feet left to be traveled by the vehicle before it reaches the stop associated with this +/// prediction +/// @param routeId the alphanumeric designator of the route (e.g., "20" or "X9") for which this prediction was +/// generated +/// @param routeDesignator the language-specific route designator of this prediction, intended for display; identical +/// to `routeId` in practice (e.g., "20") +/// @param routeDirection the direction of travel of the route associated with this prediction (e.g., "Eastbound") +/// @param destination the final destination of the vehicle associated with this prediction +/// @param arrivalTime the predicted date and time (UTC) of a vehicle’s arrival or departure to the stop associated +/// with this prediction +/// @param delayed whether the vehicle associated with this prediction is currently delayed +/// @param metadata the metadata associated with this prediction @NullMarked public record Prediction( PredictionType predictionType, @@ -42,29 +41,26 @@ public record Prediction( @Nullable Boolean delayed, PredictionMetadata metadata ) { - /** - * Constructs a {@code Prediction}. - * - * @param predictionType the type of the prediction - * @param stopId the unique identifier of the stop for which the prediction was generated - * @param stopName the display name of the stop for which the prediction was generated - * @param vehicleId the unique identifier of the vehicle for which the prediction was generated - * @param distanceToStop the feet left to be traveled by the vehicle before it reaches the stop associated with the - * prediction - * @param routeId the alphanumeric designator of the route (e.g., "20" or "X9") for which the prediction was - * generated - * @param routeDesignator the language-specific route designator of the prediction, intended for display; - * identical to {@code routeId} in practice (e.g., "20") - * @param routeDirection the direction of travel of the route associated with the prediction (e.g., "Eastbound") - * @param destination the final destination of the vehicle associated with the prediction - * @param arrivalTime the predicted date and time (UTC) of a vehicle’s arrival or departure to the stop associated - * with the prediction - * @param delayed whether the vehicle associated with the prediction is currently delayed - * @param metadata the metadata associated with the prediction - * @throws NullPointerException if {@code predictionType}, {@code stopId}, {@code stopName}, {@code vehicleId}, - * {@code distanceToStop}, {@code routeId}, {@code routeDesignator}, {@code routeDirection}, {@code destination}, - * {@code arrivalTime}, or {@code metadata} is {@code null} - */ + /// Constructs a `Prediction`. + /// + /// @param predictionType the type of the prediction + /// @param stopId the unique identifier of the stop for which the prediction was generated + /// @param stopName the display name of the stop for which the prediction was generated + /// @param vehicleId the unique identifier of the vehicle for which the prediction was generated + /// @param distanceToStop the feet left to be traveled by the vehicle before it reaches the stop associated with + /// the prediction + /// @param routeId the alphanumeric designator of the route (e.g., "20" or "X9") for which the prediction was + /// generated + /// @param routeDesignator the language-specific route designator of the prediction, intended for display; + /// identical to `routeId` in practice (e.g., "20") + /// @param routeDirection the direction of travel of the route associated with the prediction (e.g., "Eastbound") + /// @param destination the final destination of the vehicle associated with the prediction + /// @param arrivalTime the predicted date and time (UTC) of a vehicle’s arrival or departure to the stop associated + /// with the prediction + /// @param delayed whether the vehicle associated with the prediction is currently delayed + /// @param metadata the metadata associated with the prediction + /// @throws NullPointerException if `predictionType`, `stopId`, `stopName`, `vehicleId`, `distanceToStop`, + /// `routeId`, `routeDesignator`, `routeDirection`, `destination`, `arrivalTime`, or `metadata` is `null` public Prediction { Objects.requireNonNull(predictionType); Objects.requireNonNull(stopId); @@ -79,11 +75,9 @@ public record Prediction( Objects.requireNonNull(metadata); } - /** - * Calculates the estimated time of arrival (ETA) in minutes from the current time to the predicted arrival time. - * - * @return the ETA in minutes; returns 0 if the predicted arrival time is in the past - */ + /// Calculates the estimated time of arrival (ETA) in minutes from the current time to the predicted arrival time. + /// + /// @return the ETA in minutes; returns 0 if the predicted arrival time is in the past public long etaMinutes() { Instant now = Instant.now(); diff --git a/src/main/java/com/cta4j/bus/prediction/model/PredictionMetadata.java b/src/main/java/com/cta4j/bus/prediction/model/PredictionMetadata.java index 4c3fdd58..37c21286 100644 --- a/src/main/java/com/cta4j/bus/prediction/model/PredictionMetadata.java +++ b/src/main/java/com/cta4j/bus/prediction/model/PredictionMetadata.java @@ -7,29 +7,25 @@ import java.time.LocalDate; import java.util.Objects; -/** - * Represents metadata associated with a bus arrival prediction. - * - *

- * NOTE: {@code gtfsSequence} and {@code nextBus} are not well-documented by the CTA. As such, their - * presence here is primarily for completeness and may not be populated or described correctly. - *

- * - * @param timestamp the date and time (UTC) this prediction was generated - * @param dynamicAction the {@link DynamicAction} affecting this prediction - * @param blockId the scheduled block identifier for the vehicle associated with this prediction - * @param tripId the scheduled trip identifier for the vehicle associated with this prediction - * @param originalTripNumber the trip identifier for the vehicle associated with this prediction - * @param countdownLabel the countdown label associated with this prediction (e.g., "10", "DUE") - * @param zone the zone name for the vehicle associated with this prediction, otherwise blank - * @param passengerLoad the {@link PassengerLoad} of the vehicle associated with this prediction - * @param gtfsSequence the GTFS sequence number associated with this prediction, if applicable - * @param nextBus the next bus identifier associated with this prediction, if applicable - * @param scheduledStartSeconds the scheduled start time in seconds past midnight associated with this prediction, if - * applicable - * @param scheduledStartDate the scheduled start date associated with this prediction, if applicable - * @param flagStop the {@link FlagStop} information of the vehicle associated with this prediction - */ +/// Represents metadata associated with a bus arrival prediction. +/// +/// **NOTE:** `gtfsSequence` and `nextBus` are not well-documented by the CTA. As such, their presence here is +/// primarily for completeness and may not be populated or described correctly. +/// +/// @param timestamp the date and time (UTC) this prediction was generated +/// @param dynamicAction the [DynamicAction] affecting this prediction +/// @param blockId the scheduled block identifier for the vehicle associated with this prediction +/// @param tripId the scheduled trip identifier for the vehicle associated with this prediction +/// @param originalTripNumber the trip identifier for the vehicle associated with this prediction +/// @param countdownLabel the countdown label associated with this prediction (e.g., "10", "DUE") +/// @param zone the zone name for the vehicle associated with this prediction, otherwise blank +/// @param passengerLoad the [PassengerLoad] of the vehicle associated with this prediction +/// @param gtfsSequence the GTFS sequence number associated with this prediction, if applicable +/// @param nextBus the next bus identifier associated with this prediction, if applicable +/// @param scheduledStartSeconds the scheduled start time in seconds past midnight associated with this prediction, if +/// applicable +/// @param scheduledStartDate the scheduled start date associated with this prediction, if applicable +/// @param flagStop the [FlagStop] information of the vehicle associated with this prediction @NullMarked public record PredictionMetadata( Instant timestamp, @@ -46,26 +42,24 @@ public record PredictionMetadata( @Nullable LocalDate scheduledStartDate, FlagStop flagStop ) { - /** - * Constructs a {@code PredictionMetadata}. - * - * @param timestamp the date and time (UTC) the prediction was generated - * @param dynamicAction the {@link DynamicAction} affecting the prediction - * @param blockId the scheduled block identifier for the vehicle associated with the prediction - * @param tripId the scheduled trip identifier for the vehicle associated with the prediction - * @param originalTripNumber the trip identifier for the vehicle associated with the prediction - * @param countdownLabel the countdown label associated with the prediction (e.g., "10", "DUE") - * @param zone the zone name for the vehicle associated with the prediction, otherwise blank - * @param passengerLoad the {@link PassengerLoad} of the vehicle associated with the prediction - * @param gtfsSequence the GTFS sequence number associated with the prediction, if applicable - * @param nextBus the next bus identifier associated with the prediction, if applicable - * @param scheduledStartSeconds the scheduled start time in seconds past midnight associated with the prediction, - * if applicable - * @param scheduledStartDate the scheduled start date associated with the prediction, if applicable - * @param flagStop the {@link FlagStop} information of the vehicle associated with the prediction - * @throws NullPointerException if {@code timestamp}, {@code dynamicAction}, {@code blockId}, {@code tripId}, - * {@code originalTripNumber}, {@code zone}, {@code passengerLoad}, or {@code flagStop} is {@code null} - */ + /// Constructs a `PredictionMetadata`. + /// + /// @param timestamp the date and time (UTC) the prediction was generated + /// @param dynamicAction the [DynamicAction] affecting the prediction + /// @param blockId the scheduled block identifier for the vehicle associated with the prediction + /// @param tripId the scheduled trip identifier for the vehicle associated with the prediction + /// @param originalTripNumber the trip identifier for the vehicle associated with the prediction + /// @param countdownLabel the countdown label associated with the prediction (e.g., "10", "DUE") + /// @param zone the zone name for the vehicle associated with the prediction, otherwise blank + /// @param passengerLoad the [PassengerLoad] of the vehicle associated with the prediction + /// @param gtfsSequence the GTFS sequence number associated with the prediction, if applicable + /// @param nextBus the next bus identifier associated with the prediction, if applicable + /// @param scheduledStartSeconds the scheduled start time in seconds past midnight associated with the prediction, + /// if applicable + /// @param scheduledStartDate the scheduled start date associated with the prediction, if applicable + /// @param flagStop the [FlagStop] information of the vehicle associated with the prediction + /// @throws NullPointerException if `timestamp`, `dynamicAction`, `blockId`, `tripId`, `originalTripNumber`, + /// `zone`, `passengerLoad`, or `flagStop` is `null` public PredictionMetadata { Objects.requireNonNull(timestamp); Objects.requireNonNull(dynamicAction); diff --git a/src/main/java/com/cta4j/bus/prediction/model/PredictionType.java b/src/main/java/com/cta4j/bus/prediction/model/PredictionType.java index 7a6d5fc1..437fc00f 100644 --- a/src/main/java/com/cta4j/bus/prediction/model/PredictionType.java +++ b/src/main/java/com/cta4j/bus/prediction/model/PredictionType.java @@ -2,18 +2,12 @@ import org.jspecify.annotations.NullMarked; -/** - * Represents the type of bus prediction. - */ +/// Represents the type of bus prediction. @NullMarked public enum PredictionType { - /** - * Indicates an arrival prediction. - */ + /// Indicates an arrival prediction. ARRIVAL, - /** - * Indicates a departure prediction. - */ + /// Indicates a departure prediction. DEPARTURE } diff --git a/src/main/java/com/cta4j/bus/prediction/query/StopPredictionsQuery.java b/src/main/java/com/cta4j/bus/prediction/query/StopPredictionsQuery.java index 236a7b89..253c3992 100644 --- a/src/main/java/com/cta4j/bus/prediction/query/StopPredictionsQuery.java +++ b/src/main/java/com/cta4j/bus/prediction/query/StopPredictionsQuery.java @@ -8,30 +8,25 @@ import java.util.List; import java.util.Objects; -/** - * Represents a query for bus arrival predictions. - * - * @param stopIds the {@link List} of stop IDs to retrieve predictions for - * @param routeIds the optional {@link List} of route IDs to filter predictions by - * @param maxResults the optional maximum number of predictions to return - */ +/// Represents a query for bus arrival predictions. +/// +/// @param stopIds the [List] of stop IDs to retrieve predictions for +/// @param routeIds the optional [List] of route IDs to filter predictions by +/// @param maxResults the optional maximum number of predictions to return @NullMarked public record StopPredictionsQuery( List stopIds, @Nullable List routeIds, @Nullable Integer maxResults ) { - /** - * Constructs a {@code StopPredictionsQuery}. - * - * @param stopIds the {@link List} of stop IDs to retrieve predictions for - * @param routeIds the optional {@link List} of route IDs to filter predictions by - * @param maxResults the optional maximum number of predictions to return - * @throws NullPointerException if {@code stopIds} is {@code null}, or if any element of {@code stopIds} or - * {@code routeIds} is {@code null} - * @throws IllegalArgumentException if more than 10 stop IDs are provided, or if {@code maxResults} is - * non-{@code null} and not positive - */ + /// Constructs a `StopPredictionsQuery`. + /// + /// @param stopIds the [List] of stop IDs to retrieve predictions for + /// @param routeIds the optional [List] of route IDs to filter predictions by + /// @param maxResults the optional maximum number of predictions to return + /// @throws NullPointerException if `stopIds` is `null`, or if any element of `stopIds` or `routeIds` is `null` + /// @throws IllegalArgumentException if more than 10 stop IDs are provided, or if `maxResults` is non-`null` and + /// not positive public StopPredictionsQuery { Objects.requireNonNull(stopIds); @@ -48,60 +43,43 @@ public record StopPredictionsQuery( } } - /** - * Creates a builder for {@code StopPredictionsQuery}. - * - * @param stopIds the {@link Collection} of stop IDs to retrieve predictions for - * @return a new {@code Builder} instance - * @throws NullPointerException if {@code stopIds} is {@code null}, or if any element of {@code stopIds} is - * {@code null} - */ + /// Creates a builder for `StopPredictionsQuery`. + /// + /// @param stopIds the [Collection] of stop IDs to retrieve predictions for + /// @return a new `Builder` instance + /// @throws NullPointerException if `stopIds` is `null`, or if any element of `stopIds` is `null` public static Builder builder(Collection stopIds) { return new Builder(stopIds); } - /** - * A builder for {@code StopPredictionsQuery}. - */ + /// A builder for `StopPredictionsQuery`. public static final class Builder { - /** - * The {@link List} of stop IDs to retrieve predictions for. - */ + /// The [List] of stop IDs to retrieve predictions for. private final List stopIds; - /** - * The optional {@link List} of route IDs to filter predictions by. - */ + /// The optional [List] of route IDs to filter predictions by. @Nullable private List routeIds; - /** - * The optional maximum number of predictions to return. - */ + /// The optional maximum number of predictions to return. @Nullable private Integer maxResults; - /** - * Constructs a {@code Builder}. - * - * @param stopIds the {@link Collection} of stop IDs to retrieve predictions for - * @throws NullPointerException if {@code stopIds} is {@code null}, or if any element of {@code stopIds} is - * {@code null} - */ + /// Constructs a `Builder`. + /// + /// @param stopIds the [Collection] of stop IDs to retrieve predictions for + /// @throws NullPointerException if `stopIds` is `null`, or if any element of `stopIds` is `null` public Builder(Collection stopIds) { Objects.requireNonNull(stopIds); this.stopIds = List.copyOf(stopIds); } - /** - * Sets the {@link Collection} of route IDs to filter predictions by. - * - * @param routeIds the {@link Collection} of route IDs - * @return this {@code Builder} instance - * @throws NullPointerException if {@code routeIds} is {@code null}, or if any element of {@code routeIds} is - * {@code null} - */ + /// Sets the [Collection] of route IDs to filter predictions by. + /// + /// @param routeIds the [Collection] of route IDs + /// @return this `Builder` instance + /// @throws NullPointerException if `routeIds` is `null`, or if any element of `routeIds` is `null` public Builder routeIds(Collection routeIds) { Objects.requireNonNull(routeIds); @@ -110,13 +88,11 @@ public Builder routeIds(Collection routeIds) { return this; } - /** - * Sets the maximum number of predictions to return. - * - * @param maxResults the maximum number of predictions - * @return this {@code Builder} instance - * @throws IllegalArgumentException if {@code maxResults} is not positive - */ + /// Sets the maximum number of predictions to return. + /// + /// @param maxResults the maximum number of predictions + /// @return this `Builder` instance + /// @throws IllegalArgumentException if `maxResults` is not positive public Builder maxResults(int maxResults) { if (maxResults <= 0) { throw new IllegalArgumentException("maxResults must be positive"); @@ -127,12 +103,10 @@ public Builder maxResults(int maxResults) { return this; } - /** - * Builds the {@code StopPredictionsQuery}. - * - * @return a new {@code StopPredictionsQuery} instance - * @throws IllegalArgumentException if more than 10 stop IDs are provided - */ + /// Builds the `StopPredictionsQuery`. + /// + /// @return a new `StopPredictionsQuery` instance + /// @throws IllegalArgumentException if more than 10 stop IDs are provided public StopPredictionsQuery build() { return new StopPredictionsQuery( this.stopIds, diff --git a/src/main/java/com/cta4j/bus/prediction/query/VehiclePredictionsQuery.java b/src/main/java/com/cta4j/bus/prediction/query/VehiclePredictionsQuery.java index 63aa0d0d..4aac35de 100644 --- a/src/main/java/com/cta4j/bus/prediction/query/VehiclePredictionsQuery.java +++ b/src/main/java/com/cta4j/bus/prediction/query/VehiclePredictionsQuery.java @@ -8,27 +8,22 @@ import java.util.List; import java.util.Objects; -/** - * Represents a query for vehicle arrival predictions. - * - * @param vehicleIds the {@link List} of vehicle IDs to retrieve predictions for - * @param maxResults the optional maximum number of predictions to return - */ +/// Represents a query for vehicle arrival predictions. +/// +/// @param vehicleIds the [List] of vehicle IDs to retrieve predictions for +/// @param maxResults the optional maximum number of predictions to return @NullMarked public record VehiclePredictionsQuery( List vehicleIds, @Nullable Integer maxResults ) { - /** - * Constructs a {@code VehiclePredictionsQuery}. - * - * @param vehicleIds the {@link List} of vehicle IDs to retrieve predictions for - * @param maxResults the optional maximum number of predictions to return - * @throws NullPointerException if {@code vehicleIds} is {@code null}, or if any element of {@code vehicleIds} is - * {@code null} - * @throws IllegalArgumentException if more than 10 vehicle IDs are provided, or if {@code maxResults} is - * non-{@code null} and not positive - */ + /// Constructs a `VehiclePredictionsQuery`. + /// + /// @param vehicleIds the [List] of vehicle IDs to retrieve predictions for + /// @param maxResults the optional maximum number of predictions to return + /// @throws NullPointerException if `vehicleIds` is `null`, or if any element of `vehicleIds` is `null` + /// @throws IllegalArgumentException if more than 10 vehicle IDs are provided, or if `maxResults` is non-`null` and + /// not positive public VehiclePredictionsQuery { Objects.requireNonNull(vehicleIds); @@ -41,53 +36,39 @@ public record VehiclePredictionsQuery( } } - /** - * Creates a builder for {@code VehiclePredictionsQuery}. - * - * @param vehicleIds the {@link Collection} of vehicle IDs to retrieve predictions for - * @return a new {@code Builder} instance - * @throws NullPointerException if {@code vehicleIds} is {@code null}, or if any element of {@code vehicleIds} is - * {@code null} - */ + /// Creates a builder for `VehiclePredictionsQuery`. + /// + /// @param vehicleIds the [Collection] of vehicle IDs to retrieve predictions for + /// @return a new `Builder` instance + /// @throws NullPointerException if `vehicleIds` is `null`, or if any element of `vehicleIds` is `null` public static Builder builder(Collection vehicleIds) { return new Builder(vehicleIds); } - /** - * Builder for {@code VehiclePredictionsQuery}. - */ + /// Builder for `VehiclePredictionsQuery`. public static final class Builder { - /** - * The {@link List} of vehicle IDs to retrieve predictions for. - */ + /// The [List] of vehicle IDs to retrieve predictions for. private final List vehicleIds; - /** - * The optional maximum number of predictions to return. - */ + /// The optional maximum number of predictions to return. @Nullable private Integer maxResults; - /** - * Constructs a {@code Builder}. - * - * @param vehicleIds the {@link Collection} of vehicle IDs to retrieve predictions for - * @throws NullPointerException if {@code vehicleIds} is {@code null}, or if any element of - * {@code vehicleIds} is {@code null} - */ + /// Constructs a `Builder`. + /// + /// @param vehicleIds the [Collection] of vehicle IDs to retrieve predictions for + /// @throws NullPointerException if `vehicleIds` is `null`, or if any element of `vehicleIds` is `null` public Builder(Collection vehicleIds) { Objects.requireNonNull(vehicleIds); this.vehicleIds = List.copyOf(vehicleIds); } - /** - * Sets the maximum number of predictions to return. - * - * @param maxResults the maximum number of predictions to return - * @return this {@code Builder} instance - * @throws IllegalArgumentException if {@code maxResults} is not positive - */ + /// Sets the maximum number of predictions to return. + /// + /// @param maxResults the maximum number of predictions to return + /// @return this `Builder` instance + /// @throws IllegalArgumentException if `maxResults` is not positive public Builder maxResults(int maxResults) { if (maxResults <= 0) { throw new IllegalArgumentException("maxResults must be positive"); @@ -98,12 +79,10 @@ public Builder maxResults(int maxResults) { return this; } - /** - * Builds the {@code VehiclePredictionsQuery}. - * - * @return the constructed {@code VehiclePredictionsQuery} - * @throws IllegalArgumentException if more than 10 vehicle IDs are provided - */ + /// Builds the `VehiclePredictionsQuery`. + /// + /// @return the constructed `VehiclePredictionsQuery` + /// @throws IllegalArgumentException if more than 10 vehicle IDs are provided public VehiclePredictionsQuery build() { return new VehiclePredictionsQuery( this.vehicleIds, diff --git a/src/main/java/com/cta4j/bus/route/RoutesApi.java b/src/main/java/com/cta4j/bus/route/RoutesApi.java index 0137ee13..65fe1dc1 100644 --- a/src/main/java/com/cta4j/bus/route/RoutesApi.java +++ b/src/main/java/com/cta4j/bus/route/RoutesApi.java @@ -6,18 +6,14 @@ import java.util.List; -/** - * Provides access to route-related endpoints of the CTA BusTime API. - *

- * This API allows retrieval of all available routes. - */ +/// Provides access to route-related endpoints of the CTA BusTime API. +/// +/// This API allows retrieval of all available routes. @NullMarked public interface RoutesApi { - /** - * Retrieves all available routes. - * - * @return a {@link List} of all available {@link Route}s, or an empty {@link List} if no routes are found - * @throws Cta4jBusException if the API returns an error response or the response cannot be parsed - */ + /// Retrieves all available routes. + /// + /// @return a [List] of all available [Route]s, or an empty [List] if no routes are found + /// @throws Cta4jBusException if the API returns an error response or the response cannot be parsed List list(); } diff --git a/src/main/java/com/cta4j/bus/route/model/Route.java b/src/main/java/com/cta4j/bus/route/model/Route.java index 91cc3cdd..a571e3dc 100644 --- a/src/main/java/com/cta4j/bus/route/model/Route.java +++ b/src/main/java/com/cta4j/bus/route/model/Route.java @@ -5,21 +5,17 @@ import java.util.Objects; -/** - * Represents a bus route. - * - *

- * NOTE: {@code dataFeed} is not well-documented by the CTA. As such, its presence here is primarily for - * completeness and may not be populated or described correctly. - *

- * - * @param id the alphanumeric designator of this route (e.g., "22", "J14", "X9") - * @param name the common name of this route (e.g., "Clark", "Jeffery Jump", "Ashland Express") - * @param color the color of this route used in maps (e.g., "#ffffff") - * @param designator the language-specific route designator of this route, intended for display; identical to - * {@code id} in practice (e.g., "22") - * @param dataFeed the data feed identifier for this route, if applicable - */ +/// Represents a bus route. +/// +/// **NOTE:** `dataFeed` is not well-documented by the CTA. As such, its presence here is primarily for completeness +/// and may not be populated or described correctly. +/// +/// @param id the alphanumeric designator of this route (e.g., "22", "J14", "X9") +/// @param name the common name of this route (e.g., "Clark", "Jeffery Jump", "Ashland Express") +/// @param color the color of this route used in maps (e.g., "#ffffff") +/// @param designator the language-specific route designator of this route, intended for display; identical to `id` in +/// practice (e.g., "22") +/// @param dataFeed the data feed identifier for this route, if applicable @NullMarked public record Route( String id, @@ -28,17 +24,15 @@ public record Route( String designator, @Nullable String dataFeed ) { - /** - * Constructs a {@code Route}. - * - * @param id the alphanumeric designator of the route (e.g., "22", "J14", "X9") - * @param name the common name of the route (e.g., "Clark", "Jeffery Jump", "Ashland Express") - * @param color the color of the route used in maps (e.g., "#ffffff") - * @param designator the language-specific route designator of the route, intended for display; identical to - * {@code id} in practice (e.g., "22") - * @param dataFeed the data feed identifier for the route, if applicable - * @throws NullPointerException if {@code id}, {@code name}, {@code color}, or {@code designator} is {@code null} - */ + /// Constructs a `Route`. + /// + /// @param id the alphanumeric designator of the route (e.g., "22", "J14", "X9") + /// @param name the common name of the route (e.g., "Clark", "Jeffery Jump", "Ashland Express") + /// @param color the color of the route used in maps (e.g., "#ffffff") + /// @param designator the language-specific route designator of the route, intended for display; identical to `id` + /// in practice (e.g., "22") + /// @param dataFeed the data feed identifier for the route, if applicable + /// @throws NullPointerException if `id`, `name`, `color`, or `designator` is `null` public Route { Objects.requireNonNull(id); Objects.requireNonNull(name); diff --git a/src/main/java/com/cta4j/bus/stop/StopsApi.java b/src/main/java/com/cta4j/bus/stop/StopsApi.java index 8ece0856..446dbe07 100644 --- a/src/main/java/com/cta4j/bus/stop/StopsApi.java +++ b/src/main/java/com/cta4j/bus/stop/StopsApi.java @@ -10,35 +10,28 @@ import java.util.Objects; import java.util.Optional; -/** - * Provides access to stop-related endpoints of the CTA BusTime API. - *

- * This API allows retrieval of stops by route ID and direction, as well as by stop IDs. - */ +/// Provides access to stop-related endpoints of the CTA BusTime API. +/// +/// This API allows retrieval of stops by route ID and direction, as well as by stop IDs. @NullMarked public interface StopsApi { - /** - * Retrieves stops by their IDs. - * - * @param stopIds a {@link Collection} of stop IDs - * @return a {@link List} of {@link Stop}s corresponding to the provided stop IDs, or an empty {@link List} if no - * stops are found - * @throws NullPointerException if {@code stopIds} is {@code null} or contains {@code null} elements - * @throws IllegalArgumentException if more than 10 stop IDs are provided - * @throws Cta4jBusException if the API returns an error response or the response cannot be parsed - */ + /// Retrieves stops by their IDs. + /// + /// @param stopIds a [Collection] of stop IDs + /// @return a [List] of [Stop]s corresponding to the provided stop IDs, or an empty [List] if no stops are found + /// @throws NullPointerException if `stopIds` is `null` or contains `null` elements + /// @throws IllegalArgumentException if more than 10 stop IDs are provided + /// @throws Cta4jBusException if the API returns an error response or the response cannot be parsed List findByIds(Collection stopIds); - /** - * Retrieves a stop by its ID. - * - * @param stopId the stop ID - * @return an {@link Optional} containing the {@link Stop} if found, or an empty {@link Optional} if no stop is - * found for the given ID - * @throws NullPointerException if {@code stopId} is {@code null} - * @throws Cta4jBusException if multiple stops are found for the given ID, or if the API returns an error - * response or the response cannot be parsed - */ + /// Retrieves a stop by its ID. + /// + /// @param stopId the stop ID + /// @return an [Optional] containing the [Stop] if found, or an empty [Optional] if no stop is found for the given + /// ID + /// @throws NullPointerException if `stopId` is `null` + /// @throws Cta4jBusException if multiple stops are found for the given ID, or if the API returns an error response + /// or the response cannot be parsed default Optional findById(String stopId) { Objects.requireNonNull(stopId); @@ -61,15 +54,13 @@ default Optional findById(String stopId) { return Optional.of(stop); } - /** - * Retrieves stops by route ID and direction. - * - * @param routeId the route ID - * @param direction the direction (e.g., "Northbound", "Southbound") - * @return a {@link List} of {@link Stop}s corresponding to the provided route ID and direction, or an empty - * {@link List} if no stops are found - * @throws NullPointerException if {@code routeId} or {@code direction} is {@code null} - * @throws Cta4jBusException if the API returns an error response or the response cannot be parsed - */ + /// Retrieves stops by route ID and direction. + /// + /// @param routeId the route ID + /// @param direction the direction (e.g., "Northbound", "Southbound") + /// @return a [List] of [Stop]s corresponding to the provided route ID and direction, or an empty [List] if no + /// stops are found + /// @throws NullPointerException if `routeId` or `direction` is `null` + /// @throws Cta4jBusException if the API returns an error response or the response cannot be parsed List findByRouteIdAndDirection(String routeId, String direction); } diff --git a/src/main/java/com/cta4j/bus/stop/model/Stop.java b/src/main/java/com/cta4j/bus/stop/model/Stop.java index a9efa608..304c2abf 100644 --- a/src/main/java/com/cta4j/bus/stop/model/Stop.java +++ b/src/main/java/com/cta4j/bus/stop/model/Stop.java @@ -7,23 +7,19 @@ import java.util.List; import java.util.Objects; -/** - * Represents a bus stop. - * - *

- * NOTE: {@code gtfsSequence} is not well-documented by the CTA. As such, its presence here is primarily for - * completeness and may not be populated or described correctly. - *

- * - * @param id the unique identifier of this stop - * @param name the display name of this stop (e.g., "Clark & Addison") - * @param latitude the latitude coordinate of this stop - * @param longitude the longitude coordinate of this stop - * @param detoursAdded the {@link List} of detour IDs which temporarily add service to this stop - * @param detoursRemoved the {@link List} of detour IDs which temporarily remove service from this stop - * @param gtfsSequence the GTFS sequence number of this stop, if applicable - * @param adaAccessible whether this stop is ADA accessible, if known - */ +/// Represents a bus stop. +/// +/// **NOTE:** `gtfsSequence` is not well-documented by the CTA. As such, its presence here is primarily for +/// completeness and may not be populated or described correctly. +/// +/// @param id the unique identifier of this stop +/// @param name the display name of this stop (e.g., "Clark & Addison") +/// @param latitude the latitude coordinate of this stop +/// @param longitude the longitude coordinate of this stop +/// @param detoursAdded the [List] of detour IDs which temporarily add service to this stop +/// @param detoursRemoved the [List] of detour IDs which temporarily remove service from this stop +/// @param gtfsSequence the GTFS sequence number of this stop, if applicable +/// @param adaAccessible whether this stop is ADA accessible, if known @NullMarked public record Stop( String id, @@ -35,20 +31,18 @@ public record Stop( @Nullable Integer gtfsSequence, @Nullable Boolean adaAccessible ) { - /** - * Constructs a {@code Stop}. - * - * @param id the unique identifier of the stop - * @param name the display name of the stop (e.g., "Clark & Addison") - * @param latitude the latitude coordinate of the stop - * @param longitude the longitude coordinate of the stop - * @param detoursAdded the {@link List} of detour IDs which temporarily add service to the stop - * @param detoursRemoved the {@link List} of detour IDs which temporarily remove service from the stop - * @param gtfsSequence the GTFS sequence number of the stop, if applicable - * @param adaAccessible whether the stop is ADA accessible, if known - * @throws NullPointerException if {@code id}, {@code name}, {@code latitude}, or {@code longitude} is - * {@code null}, or if any element of {@code detoursAdded} or {@code detoursRemoved} is {@code null} - */ + /// Constructs a `Stop`. + /// + /// @param id the unique identifier of the stop + /// @param name the display name of the stop (e.g., "Clark & Addison") + /// @param latitude the latitude coordinate of the stop + /// @param longitude the longitude coordinate of the stop + /// @param detoursAdded the [List] of detour IDs which temporarily add service to the stop + /// @param detoursRemoved the [List] of detour IDs which temporarily remove service from the stop + /// @param gtfsSequence the GTFS sequence number of the stop, if applicable + /// @param adaAccessible whether the stop is ADA accessible, if known + /// @throws NullPointerException if `id`, `name`, `latitude`, or `longitude` is `null`, or if any element of + /// `detoursAdded` or `detoursRemoved` is `null` public Stop { Objects.requireNonNull(id); Objects.requireNonNull(name); diff --git a/src/main/java/com/cta4j/bus/vehicle/VehiclesApi.java b/src/main/java/com/cta4j/bus/vehicle/VehiclesApi.java index 4a93e5d9..b600fd55 100644 --- a/src/main/java/com/cta4j/bus/vehicle/VehiclesApi.java +++ b/src/main/java/com/cta4j/bus/vehicle/VehiclesApi.java @@ -10,35 +10,28 @@ import java.util.Objects; import java.util.Optional; -/** - * Provides access to vehicle-related endpoints of the CTA BusTime API. - *

- * This API allows retrieval of vehicles by their IDs or by associated route IDs. - */ +/// Provides access to vehicle-related endpoints of the CTA BusTime API. +/// +/// This API allows retrieval of vehicles by their IDs or by associated route IDs. @NullMarked public interface VehiclesApi { - /** - * Retrieves vehicles by their IDs. - * - * @param ids a {@link Collection} of vehicle IDs - * @return a {@link List} of {@link Vehicle}s corresponding to the provided IDs, or an empty {@link List} if no - * vehicles are found - * @throws NullPointerException if {@code ids} is {@code null} or contains {@code null} elements - * @throws IllegalArgumentException if more than 10 vehicle IDs are provided - * @throws Cta4jBusException if the API returns an error response or the response cannot be parsed - */ + /// Retrieves vehicles by their IDs. + /// + /// @param ids a [Collection] of vehicle IDs + /// @return a [List] of [Vehicle]s corresponding to the provided IDs, or an empty [List] if no vehicles are found + /// @throws NullPointerException if `ids` is `null` or contains `null` elements + /// @throws IllegalArgumentException if more than 10 vehicle IDs are provided + /// @throws Cta4jBusException if the API returns an error response or the response cannot be parsed List findByIds(Collection ids); - /** - * Retrieves a vehicle by its ID. - * - * @param id the vehicle ID - * @return an {@link Optional} containing the {@link Vehicle} if found, or an empty {@link Optional} if no vehicle - * is found for the given ID - * @throws NullPointerException if {@code id} is {@code null} - * @throws Cta4jBusException if multiple vehicles are found for the given ID, or if the API returns an error - * response or the response cannot be parsed - */ + /// Retrieves a vehicle by its ID. + /// + /// @param id the vehicle ID + /// @return an [Optional] containing the [Vehicle] if found, or an empty [Optional] if no vehicle is found for the + /// given ID + /// @throws NullPointerException if `id` is `null` + /// @throws Cta4jBusException if multiple vehicles are found for the given ID, or if the API returns an error + /// response or the response cannot be parsed default Optional findById(String id) { Objects.requireNonNull(id); @@ -64,27 +57,23 @@ default Optional findById(String id) { return Optional.of(vehicle); } - /** - * Retrieves all vehicles for the specified route IDs. - * - * @param routeIds a {@link Collection} of route IDs - * @return a {@link List} of {@link Vehicle}s associated with the route IDs, or an empty {@link List} if no - * vehicles are found for the route IDs - * @throws NullPointerException if {@code routeIds} is {@code null} or contains {@code null} elements - * @throws IllegalArgumentException if more than 10 route IDs are provided - * @throws Cta4jBusException if the API returns an error response or the response cannot be parsed - */ + /// Retrieves all vehicles for the specified route IDs. + /// + /// @param routeIds a [Collection] of route IDs + /// @return a [List] of [Vehicle]s associated with the route IDs, or an empty [List] if no vehicles are found for + /// the route IDs + /// @throws NullPointerException if `routeIds` is `null` or contains `null` elements + /// @throws IllegalArgumentException if more than 10 route IDs are provided + /// @throws Cta4jBusException if the API returns an error response or the response cannot be parsed List findByRouteIds(Collection routeIds); - /** - * Retrieves all vehicles for the specified route ID. - * - * @param routeId the route ID - * @return a {@link List} of {@link Vehicle}s associated with the route ID, or an empty {@link List} if no vehicles - * are found for the route ID - * @throws NullPointerException if {@code routeId} is {@code null} - * @throws Cta4jBusException if the API returns an error response or the response cannot be parsed - */ + /// Retrieves all vehicles for the specified route ID. + /// + /// @param routeId the route ID + /// @return a [List] of [Vehicle]s associated with the route ID, or an empty [List] if no vehicles are found for + /// the route ID + /// @throws NullPointerException if `routeId` is `null` + /// @throws Cta4jBusException if the API returns an error response or the response cannot be parsed default List findByRouteId(String routeId) { Objects.requireNonNull(routeId); diff --git a/src/main/java/com/cta4j/bus/vehicle/model/TransitMode.java b/src/main/java/com/cta4j/bus/vehicle/model/TransitMode.java index 8dede895..c93c35f5 100644 --- a/src/main/java/com/cta4j/bus/vehicle/model/TransitMode.java +++ b/src/main/java/com/cta4j/bus/vehicle/model/TransitMode.java @@ -2,34 +2,22 @@ import org.jspecify.annotations.NullMarked; -/** - * Represents the mode of transit for a vehicle. - */ +/// Represents the mode of transit for a vehicle. @NullMarked public enum TransitMode { - /** - * Indicates no specific transit mode. - */ + /// Indicates no specific transit mode. NONE(0), - /** - * Indicates a bus transit mode. - */ + /// Indicates a bus transit mode. BUS(1), - /** - * Indicates a ferry transit mode. - */ + /// Indicates a ferry transit mode. FERRY(2), - /** - * Indicates a rail transit mode. - */ + /// Indicates a rail transit mode. RAIL(3), - /** - * Indicates a people mover transit mode. - */ + /// Indicates a people mover transit mode. PEOPLE_MOVER(4); private final int code; @@ -38,22 +26,18 @@ public enum TransitMode { this.code = code; } - /** - * Gets the code associated with this transit mode. - * - * @return the transit mode code - */ + /// Gets the code associated with this transit mode. + /// + /// @return the transit mode code public int getCode() { return this.code; } - /** - * Returns the {@code TransitMode} corresponding to the given code. - * - * @param code the transit mode code - * @return the corresponding {@code TransitMode} - * @throws IllegalArgumentException if the code does not correspond to any known transit mode - */ + /// Returns the `TransitMode` corresponding to the given code. + /// + /// @param code the transit mode code + /// @return the corresponding `TransitMode` + /// @throws IllegalArgumentException if the code does not correspond to any known transit mode public static TransitMode fromCode(int code) { return switch (code) { case 0 -> NONE; diff --git a/src/main/java/com/cta4j/bus/vehicle/model/Vehicle.java b/src/main/java/com/cta4j/bus/vehicle/model/Vehicle.java index 4af9e2ab..18e2f3f2 100644 --- a/src/main/java/com/cta4j/bus/vehicle/model/Vehicle.java +++ b/src/main/java/com/cta4j/bus/vehicle/model/Vehicle.java @@ -5,16 +5,14 @@ import java.util.Objects; -/** - * Represents a vehicle. - * - * @param id the unique identifier of this vehicle - * @param routeId the alphanumeric designator of the route that is currently being serviced by this vehicle - * @param destination the destination of the trip being serviced by this vehicle (e.g., "Howard") - * @param coordinates the current coordinates of this vehicle - * @param delayed whether this vehicle is currently delayed - * @param metadata the metadata associated with this vehicle - */ +/// Represents a vehicle. +/// +/// @param id the unique identifier of this vehicle +/// @param routeId the alphanumeric designator of the route that is currently being serviced by this vehicle +/// @param destination the destination of the trip being serviced by this vehicle (e.g., "Howard") +/// @param coordinates the current coordinates of this vehicle +/// @param delayed whether this vehicle is currently delayed +/// @param metadata the metadata associated with this vehicle @NullMarked public record Vehicle( String id, @@ -24,18 +22,15 @@ public record Vehicle( boolean delayed, VehicleMetadata metadata ) { - /** - * Constructs a {@code Vehicle}. - * - * @param id the unique identifier of the vehicle - * @param routeId the alphanumeric designator of the route that is currently being serviced by the vehicle - * @param destination the destination of the trip being serviced by the vehicle (e.g., "Howard") - * @param coordinates the current coordinates of the vehicle - * @param delayed whether the vehicle is currently delayed - * @param metadata the metadata associated with the vehicle - * @throws NullPointerException if {@code id}, {@code routeId}, {@code destination}, {@code coordinates}, or - * {@code metadata} is {@code null} - */ + /// Constructs a `Vehicle`. + /// + /// @param id the unique identifier of the vehicle + /// @param routeId the alphanumeric designator of the route that is currently being serviced by the vehicle + /// @param destination the destination of the trip being serviced by the vehicle (e.g., "Howard") + /// @param coordinates the current coordinates of the vehicle + /// @param delayed whether the vehicle is currently delayed + /// @param metadata the metadata associated with the vehicle + /// @throws NullPointerException if `id`, `routeId`, `destination`, `coordinates`, or `metadata` is `null` public Vehicle { Objects.requireNonNull(id); Objects.requireNonNull(routeId); diff --git a/src/main/java/com/cta4j/bus/vehicle/model/VehicleMetadata.java b/src/main/java/com/cta4j/bus/vehicle/model/VehicleMetadata.java index 6aef1612..82eb5ea0 100644 --- a/src/main/java/com/cta4j/bus/vehicle/model/VehicleMetadata.java +++ b/src/main/java/com/cta4j/bus/vehicle/model/VehicleMetadata.java @@ -8,37 +8,34 @@ import java.time.LocalDate; import java.util.Objects; -/** - * Represents metadata associated with a vehicle. - * - *

- * NOTE: {@code dataFeed}, {@code stopStatus}, {@code timepointId}, {@code stopId}, {@code sequence}, - * {@code gtfsSequence}, {@code serverTimestamp}, {@code speed}, and {@code block} are not well-documented by the - * CTA. As such, their presence here is primarily for completeness and may not be populated or described correctly. - *

- * - * @param dataFeed the data feed from which this vehicle information was obtained, if applicable - * @param lastUpdated the date and time (UTC) this vehicle information was last updated, if applicable - * @param patternId the pattern identifier for the trip this vehicle is servicing - * @param distanceToPatternPoint the number of feet this vehicle has traveled into the pattern currently being serviced - * @param stopStatus the stop status of this vehicle, if applicable - * @param timepointId the timepoint identifier associated with this vehicle, if applicable - * @param stopId the stop identifier associated with this vehicle, if applicable - * @param sequence the sequence number associated with this vehicle, if applicable - * @param gtfsSequence the GTFS sequence number associated with this vehicle, if applicable - * @param serverTimestamp the date and time (UTC) this vehicle information was received by the server, if applicable - * @param speed the current speed of this vehicle in miles per hour, if applicable - * @param block the block number for this vehicle, if applicable - * @param blockId the scheduled block identifier for this vehicle - * @param tripId the scheduled trip identifier for this vehicle - * @param originalTripNumber the trip identifier for this vehicle - * @param zone the zone name for this vehicle, otherwise blank - * @param mode the {@link TransitMode} of this vehicle - * @param passengerLoad the {@link PassengerLoad} of this vehicle - * @param scheduledStartSeconds the scheduled start time in seconds past midnight associated with this vehicle, if - * applicable - * @param scheduledStartDate the scheduled start date associated with this vehicle, if applicable - */ +/// Represents metadata associated with a vehicle. +/// +/// **NOTE:** `dataFeed`, `stopStatus`, `timepointId`, `stopId`, `sequence`, `gtfsSequence`, `serverTimestamp`, +/// `speed`, and `block` are not well-documented by the CTA. As such, their presence here is primarily for completeness +/// and may not be populated or described correctly. +/// +/// @param dataFeed the data feed from which this vehicle information was obtained, if applicable +/// @param lastUpdated the date and time (UTC) this vehicle information was last updated, if applicable +/// @param patternId the pattern identifier for the trip this vehicle is servicing +/// @param distanceToPatternPoint the number of feet this vehicle has traveled into the pattern currently being +/// serviced +/// @param stopStatus the stop status of this vehicle, if applicable +/// @param timepointId the timepoint identifier associated with this vehicle, if applicable +/// @param stopId the stop identifier associated with this vehicle, if applicable +/// @param sequence the sequence number associated with this vehicle, if applicable +/// @param gtfsSequence the GTFS sequence number associated with this vehicle, if applicable +/// @param serverTimestamp the date and time (UTC) this vehicle information was received by the server, if applicable +/// @param speed the current speed of this vehicle in miles per hour, if applicable +/// @param block the block number for this vehicle, if applicable +/// @param blockId the scheduled block identifier for this vehicle +/// @param tripId the scheduled trip identifier for this vehicle +/// @param originalTripNumber the trip identifier for this vehicle +/// @param zone the zone name for this vehicle, otherwise blank +/// @param mode the [TransitMode] of this vehicle +/// @param passengerLoad the [PassengerLoad] of this vehicle +/// @param scheduledStartSeconds the scheduled start time in seconds past midnight associated with this vehicle, if +/// applicable +/// @param scheduledStartDate the scheduled start date associated with this vehicle, if applicable @NullMarked public record VehicleMetadata( @Nullable String dataFeed, @@ -62,34 +59,33 @@ public record VehicleMetadata( @Nullable Integer scheduledStartSeconds, @Nullable LocalDate scheduledStartDate ) { - /** - * Constructs a {@code VehicleMetadata}. - * - * @param dataFeed the data feed from which the vehicle information was obtained, if applicable - * @param lastUpdated the date and time (UTC) the vehicle information was last updated, if applicable - * @param patternId the pattern identifier for the trip the vehicle is servicing - * @param distanceToPatternPoint the number of feet the vehicle has traveled into the pattern currently being - * serviced - * @param stopStatus the stop status of the vehicle, if applicable - * @param timepointId the timepoint identifier associated with the vehicle, if applicable - * @param stopId the stop identifier associated with the vehicle, if applicable - * @param sequence the sequence number associated with the vehicle, if applicable - * @param gtfsSequence the GTFS sequence number associated with the vehicle, if applicable - * @param serverTimestamp the date and time (UTC) the vehicle information was received by the server, if applicable - * @param speed the current speed of the vehicle in miles per hour, if applicable - * @param block the block number for the vehicle, if applicable - * @param blockId the scheduled block identifier for the vehicle - * @param tripId the scheduled trip identifier for the vehicle - * @param originalTripNumber the trip identifier for the vehicle - * @param zone the zone name for the vehicle, otherwise blank - * @param mode the {@link TransitMode} of the vehicle - * @param passengerLoad the {@link PassengerLoad} of the vehicle - * @param scheduledStartSeconds the scheduled start time in seconds past midnight associated with the vehicle, if - * applicable - * @param scheduledStartDate the scheduled start date associated with the vehicle, if applicable - * @throws NullPointerException if {@code patternId}, {@code blockId}, {@code tripId}, {@code originalTripNumber}, - * {@code zone}, {@code mode}, or {@code passengerLoad} is {@code null} - */ + /// Constructs a `VehicleMetadata`. + /// + /// @param dataFeed the data feed from which the vehicle information was obtained, if applicable + /// @param lastUpdated the date and time (UTC) the vehicle information was last updated, if applicable + /// @param patternId the pattern identifier for the trip the vehicle is servicing + /// @param distanceToPatternPoint the number of feet the vehicle has traveled into the pattern currently being + /// serviced + /// @param stopStatus the stop status of the vehicle, if applicable + /// @param timepointId the timepoint identifier associated with the vehicle, if applicable + /// @param stopId the stop identifier associated with the vehicle, if applicable + /// @param sequence the sequence number associated with the vehicle, if applicable + /// @param gtfsSequence the GTFS sequence number associated with the vehicle, if applicable + /// @param serverTimestamp the date and time (UTC) the vehicle information was received by the server, if + /// applicable + /// @param speed the current speed of the vehicle in miles per hour, if applicable + /// @param block the block number for the vehicle, if applicable + /// @param blockId the scheduled block identifier for the vehicle + /// @param tripId the scheduled trip identifier for the vehicle + /// @param originalTripNumber the trip identifier for the vehicle + /// @param zone the zone name for the vehicle, otherwise blank + /// @param mode the [TransitMode] of the vehicle + /// @param passengerLoad the [PassengerLoad] of the vehicle + /// @param scheduledStartSeconds the scheduled start time in seconds past midnight associated with the vehicle, if + /// applicable + /// @param scheduledStartDate the scheduled start date associated with the vehicle, if applicable + /// @throws NullPointerException if `patternId`, `blockId`, `tripId`, `originalTripNumber`, `zone`, `mode`, or + /// `passengerLoad` is `null` public VehicleMetadata { Objects.requireNonNull(patternId); Objects.requireNonNull(blockId); From 90f1df7e4e49601471e514e3f396a6577c9417a6 Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Wed, 29 Jul 2026 13:33:33 -0500 Subject: [PATCH 39/60] Markdown Javadoc comments --- .../common/exception/Cta4jException.java | 42 +++++++------------ .../com/cta4j/common/geo/Coordinates.java | 30 ++++++------- 2 files changed, 29 insertions(+), 43 deletions(-) diff --git a/src/main/java/com/cta4j/common/exception/Cta4jException.java b/src/main/java/com/cta4j/common/exception/Cta4jException.java index 3701dfe8..91881b51 100644 --- a/src/main/java/com/cta4j/common/exception/Cta4jException.java +++ b/src/main/java/com/cta4j/common/exception/Cta4jException.java @@ -4,48 +4,38 @@ import java.util.Objects; -/** - * A custom exception class for handling cta4j-specific errors. - */ +/// A custom exception class for handling cta4j-specific errors. @NullMarked public class Cta4jException extends RuntimeException { - /** - * The endpoint associated with this exception. - */ + /// The endpoint associated with this exception. private final String endpoint; - /** - * Constructs a {@code Cta4jException}. - * - * @param message the detail message - * @param endpoint the endpoint associated with the exception - * @throws NullPointerException if {@code endpoint} is {@code null} - */ + /// Constructs a `Cta4jException`. + /// + /// @param message the detail message + /// @param endpoint the endpoint associated with the exception + /// @throws NullPointerException if `endpoint` is `null` public Cta4jException(String message, String endpoint) { super(message); this.endpoint = Objects.requireNonNull(endpoint); } - /** - * Constructs a {@code Cta4jException}. - * - * @param message the detail message - * @param endpoint the endpoint associated with the exception - * @param cause the cause of the exception - * @throws NullPointerException if {@code endpoint} is {@code null} - */ + /// Constructs a `Cta4jException`. + /// + /// @param message the detail message + /// @param endpoint the endpoint associated with the exception + /// @param cause the cause of the exception + /// @throws NullPointerException if `endpoint` is `null` public Cta4jException(String message, String endpoint, Throwable cause) { super(message, cause); this.endpoint = Objects.requireNonNull(endpoint); } - /** - * Returns the endpoint associated with this exception. - * - * @return the endpoint - */ + /// Returns the endpoint associated with this exception. + /// + /// @return the endpoint public String getEndpoint() { return this.endpoint; } diff --git a/src/main/java/com/cta4j/common/geo/Coordinates.java b/src/main/java/com/cta4j/common/geo/Coordinates.java index 9d95443a..8f918d7b 100644 --- a/src/main/java/com/cta4j/common/geo/Coordinates.java +++ b/src/main/java/com/cta4j/common/geo/Coordinates.java @@ -6,29 +6,25 @@ import java.math.BigDecimal; import java.util.Objects; -/** - * Represents geographic coordinates. - * - * @param latitude the latitude of these coordinates - * @param longitude the longitude of these coordinates - * @param heading the heading of these coordinates in degrees (0-359) - */ +/// Represents geographic coordinates. +/// +/// @param latitude the latitude of these coordinates +/// @param longitude the longitude of these coordinates +/// @param heading the heading of these coordinates in degrees (0-359) @NullMarked public record Coordinates( BigDecimal latitude, BigDecimal longitude, int heading ) { - /** - * Constructs a {@code Coordinates}. - * - * @param latitude the latitude of the coordinates - * @param longitude the longitude of the coordinates - * @param heading the heading of the coordinates in degrees (0-359) - * @throws NullPointerException if {@code latitude} or {@code longitude} is {@code null} - * @throws IllegalArgumentException if {@code latitude} is not between -90 and 90 (inclusive), {@code longitude} - * is not between -180 and 180 (inclusive), or {@code heading} is not between 0 and 359 (inclusive) - */ + /// Constructs a `Coordinates`. + /// + /// @param latitude the latitude of the coordinates + /// @param longitude the longitude of the coordinates + /// @param heading the heading of the coordinates in degrees (0-359) + /// @throws NullPointerException if `latitude` or `longitude` is `null` + /// @throws IllegalArgumentException if `latitude` is not between -90 and 90 (inclusive), `longitude` is not + /// between -180 and 180 (inclusive), or `heading` is not between 0 and 359 (inclusive) public Coordinates { Objects.requireNonNull(latitude); Objects.requireNonNull(longitude); From 2890781a30de8c0b17d8d65e0618657c17ffbf0e Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Wed, 29 Jul 2026 15:18:15 -0500 Subject: [PATCH 40/60] Markdown Javadoc comments --- CLAUDE.md | 2 +- .../common/exception/Cta4jAlertException.java | 1 - .../alert/common/model/AlertTrainLine.java | 5 - .../Cta4jDetailedAlertsException.java | 1 - .../exception/DetailedAlertsErrorCode.java | 4 - .../detailedalert/query/AlertsQuery.java | 5 - .../query/BusRouteAlertsQuery.java | 6 - .../detailedalert/query/LineAlertsQuery.java | 6 - .../query/StationAlertsQuery.java | 6 - .../exception/Cta4jRouteStatusException.java | 1 - .../exception/RouteStatusErrorCode.java | 4 - .../com/cta4j/bus/detour/model/Detour.java | 4 +- .../prediction/model/PredictionMetadata.java | 4 +- .../query/StopPredictionsQuery.java | 3 - .../query/VehiclePredictionsQuery.java | 2 - .../java/com/cta4j/bus/route/model/Route.java | 4 +- .../java/com/cta4j/bus/stop/model/Stop.java | 4 +- .../bus/vehicle/model/VehicleMetadata.java | 6 +- .../common/exception/Cta4jException.java | 1 - src/main/java/com/cta4j/train/TrainApi.java | 104 +++++++----------- .../com/cta4j/train/arrival/ArrivalsApi.java | 72 ++++++------ .../arrival/exception/ArrivalsErrorCode.java | 102 +++++------------ .../exception/Cta4jArrivalsException.java | 35 ++---- .../train/arrival/query/MapArrivalsQuery.java | 95 ++++++---------- .../arrival/query/StopArrivalsQuery.java | 95 ++++++---------- .../common/exception/Cta4jTrainException.java | 55 ++++----- .../com/cta4j/train/common/model/Arrival.java | 77 ++++++------- .../train/common/model/ArrivalMetadata.java | 38 +++---- .../train/common/model/TrainDirection.java | 49 +++------ .../cta4j/train/common/model/TrainLine.java | 78 ++++--------- .../com/cta4j/train/follow/FollowApi.java | 24 ++-- .../exception/Cta4jFollowException.java | 35 ++---- .../follow/exception/FollowErrorCode.java | 72 ++++-------- .../cta4j/train/follow/model/FollowTrain.java | 23 ++-- .../cta4j/train/location/LocationsApi.java | 51 ++++----- .../exception/Cta4jLocationsException.java | 35 ++---- .../exception/LocationsErrorCode.java | 68 ++++-------- .../train/location/model/LocationTrain.java | 77 ++++++------- .../train/location/model/TrainLocations.java | 23 ++-- .../com/cta4j/train/station/StationsApi.java | 26 ++--- .../station/model/CardinalDirection.java | 32 ++---- .../train/station/model/HumanAddress.java | 30 +++-- .../cta4j/train/station/model/Location.java | 26 ++--- .../cta4j/train/station/model/Station.java | 53 ++++----- 44 files changed, 518 insertions(+), 926 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index a8d14d1d..78dd9ade 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -116,7 +116,7 @@ References: - **Summary sentence:** The first line is a standalone summary fragment ending in a period, third-person descriptive ("Returns the active arrivals for a station," not "This method returns..."). -- **Tag order:** `@param` → `@return` → `@deprecated` → `@since` → `@throws` +- **Tag order:** `@apiNote` → `@param` → `@return` → `@deprecated` → `@since` → `@throws` → `@see`. - **@param / @throws descriptions:** Lowercase phrase, no trailing period. - **Code references:** Use backtick spans (`` `RoutesApi` ``, `` `List` ``) diff --git a/src/main/java/com/cta4j/alert/common/exception/Cta4jAlertException.java b/src/main/java/com/cta4j/alert/common/exception/Cta4jAlertException.java index f6e2e376..dc3c3c23 100644 --- a/src/main/java/com/cta4j/alert/common/exception/Cta4jAlertException.java +++ b/src/main/java/com/cta4j/alert/common/exception/Cta4jAlertException.java @@ -7,7 +7,6 @@ /// A custom exception class for handling cta4j alert-specific errors. @NullMarked public class Cta4jAlertException extends Cta4jException { - /// The raw error code associated with this exception, if available. @Nullable private final Integer rawErrorCode; diff --git a/src/main/java/com/cta4j/alert/common/model/AlertTrainLine.java b/src/main/java/com/cta4j/alert/common/model/AlertTrainLine.java index 41319deb..130a9a41 100644 --- a/src/main/java/com/cta4j/alert/common/model/AlertTrainLine.java +++ b/src/main/java/com/cta4j/alert/common/model/AlertTrainLine.java @@ -38,13 +38,8 @@ public enum AlertTrainLine { /// Indicates the Yellow Line. YELLOW("Y"); - /// The CTA Alerts API route designator for this train line. private final String code; - /// Constructs an `AlertTrainLine`. - /// - /// @param code the CTA Alerts API route designator of the train line - /// @throws NullPointerException if `code` is `null` AlertTrainLine(String code) { this.code = Objects.requireNonNull(code); } diff --git a/src/main/java/com/cta4j/alert/detailedalert/exception/Cta4jDetailedAlertsException.java b/src/main/java/com/cta4j/alert/detailedalert/exception/Cta4jDetailedAlertsException.java index f4ea3ccd..e2c905e9 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/exception/Cta4jDetailedAlertsException.java +++ b/src/main/java/com/cta4j/alert/detailedalert/exception/Cta4jDetailedAlertsException.java @@ -8,7 +8,6 @@ /// A custom exception class for handling cta4j detailed alerts-specific errors. @NullMarked public final class Cta4jDetailedAlertsException extends Cta4jAlertException { - /// The error code associated with this exception, if available. @Nullable private final DetailedAlertsErrorCode errorCode; diff --git a/src/main/java/com/cta4j/alert/detailedalert/exception/DetailedAlertsErrorCode.java b/src/main/java/com/cta4j/alert/detailedalert/exception/DetailedAlertsErrorCode.java index c8176c35..355fdf7a 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/exception/DetailedAlertsErrorCode.java +++ b/src/main/java/com/cta4j/alert/detailedalert/exception/DetailedAlertsErrorCode.java @@ -49,12 +49,8 @@ public enum DetailedAlertsErrorCode { /// Indicates that an unknown error occurred that does not match any of the defined error codes. UNKNOWN(-1); - /// The integer code associated with this error code. private final int code; - /// Constructs a `DetailedAlertsErrorCode`. - /// - /// @param code the integer code associated with the error code DetailedAlertsErrorCode(int code) { this.code = code; } diff --git a/src/main/java/com/cta4j/alert/detailedalert/query/AlertsQuery.java b/src/main/java/com/cta4j/alert/detailedalert/query/AlertsQuery.java index 7b227ce7..14d15439 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/query/AlertsQuery.java +++ b/src/main/java/com/cta4j/alert/detailedalert/query/AlertsQuery.java @@ -50,20 +50,15 @@ public static Builder builder() { /// A builder for `AlertsQuery`. public static final class Builder { - /// Whether to include only alerts that are currently active. private boolean activeOnly; - /// Whether to include alerts that affect accessible paths in stations. private boolean accessibility; - /// Whether to include common planned alerts. private boolean planned; - /// The optional date; only alerts with a start date before this date are included. @Nullable private LocalDate byStartDate; - /// The optional number of days; only alerts that started within this many days of today are included. @Nullable private Integer recentDays; diff --git a/src/main/java/com/cta4j/alert/detailedalert/query/BusRouteAlertsQuery.java b/src/main/java/com/cta4j/alert/detailedalert/query/BusRouteAlertsQuery.java index 1ac16557..e84ca349 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/query/BusRouteAlertsQuery.java +++ b/src/main/java/com/cta4j/alert/detailedalert/query/BusRouteAlertsQuery.java @@ -62,23 +62,17 @@ public static Builder builder(Collection routeIds) { /// A builder for `BusRouteAlertsQuery`. public static final class Builder { - /// The [List] of bus route IDs to retrieve alerts for. private final List routeIds; - /// Whether to include only alerts that are currently active. private boolean activeOnly; - /// Whether to include alerts that affect accessible paths in stations. private boolean accessibility; - /// Whether to include common planned alerts. private boolean planned; - /// The optional date; only alerts with a start date before this date are included. @Nullable private LocalDate byStartDate; - /// The optional number of days; only alerts that started within this many days of today are included. @Nullable private Integer recentDays; diff --git a/src/main/java/com/cta4j/alert/detailedalert/query/LineAlertsQuery.java b/src/main/java/com/cta4j/alert/detailedalert/query/LineAlertsQuery.java index b8dae78d..57de75ad 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/query/LineAlertsQuery.java +++ b/src/main/java/com/cta4j/alert/detailedalert/query/LineAlertsQuery.java @@ -63,23 +63,17 @@ public static Builder builder(Collection lines) { /// A builder for `LineAlertsQuery`. public static final class Builder { - /// The [List] of [AlertTrainLine]s to retrieve alerts for. private final List lines; - /// Whether to include only alerts that are currently active. private boolean activeOnly; - /// Whether to include alerts that affect accessible paths in stations. private boolean accessibility; - /// Whether to include common planned alerts. private boolean planned; - /// The optional date; only alerts with a start date before this date are included. @Nullable private LocalDate byStartDate; - /// The optional number of days; only alerts that started within this many days of today are included. @Nullable private Integer recentDays; diff --git a/src/main/java/com/cta4j/alert/detailedalert/query/StationAlertsQuery.java b/src/main/java/com/cta4j/alert/detailedalert/query/StationAlertsQuery.java index 70ef881b..943c8e08 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/query/StationAlertsQuery.java +++ b/src/main/java/com/cta4j/alert/detailedalert/query/StationAlertsQuery.java @@ -62,23 +62,17 @@ public static Builder builder(Collection stationIds) { /// A builder for `StationAlertsQuery`. public static final class Builder { - /// The [List] of train station IDs to retrieve alerts for. private final List stationIds; - /// Whether to include only alerts that are currently active. private boolean activeOnly; - /// Whether to include alerts that affect accessible paths in stations. private boolean accessibility; - /// Whether to include common planned alerts. private boolean planned; - /// The optional date; only alerts with a start date before this date are included. @Nullable private LocalDate byStartDate; - /// The optional number of days; only alerts that started within this many days of today are included. @Nullable private Integer recentDays; diff --git a/src/main/java/com/cta4j/alert/routestatus/exception/Cta4jRouteStatusException.java b/src/main/java/com/cta4j/alert/routestatus/exception/Cta4jRouteStatusException.java index f87e6a78..0150235e 100644 --- a/src/main/java/com/cta4j/alert/routestatus/exception/Cta4jRouteStatusException.java +++ b/src/main/java/com/cta4j/alert/routestatus/exception/Cta4jRouteStatusException.java @@ -8,7 +8,6 @@ /// A custom exception class for handling cta4j alert route status-specific errors. @NullMarked public final class Cta4jRouteStatusException extends Cta4jAlertException { - /// The error code associated with this exception, if available. @Nullable private final RouteStatusErrorCode errorCode; diff --git a/src/main/java/com/cta4j/alert/routestatus/exception/RouteStatusErrorCode.java b/src/main/java/com/cta4j/alert/routestatus/exception/RouteStatusErrorCode.java index c1f58291..103ada94 100644 --- a/src/main/java/com/cta4j/alert/routestatus/exception/RouteStatusErrorCode.java +++ b/src/main/java/com/cta4j/alert/routestatus/exception/RouteStatusErrorCode.java @@ -39,12 +39,8 @@ public enum RouteStatusErrorCode { /// Indicates that an unknown error occurred that does not match any of the defined error codes. UNKNOWN(-1); - /// The integer code associated with this error code. private final int code; - /// Constructs a `RouteStatusErrorCode`. - /// - /// @param code the integer code associated with the error code RouteStatusErrorCode(int code) { this.code = code; } diff --git a/src/main/java/com/cta4j/bus/detour/model/Detour.java b/src/main/java/com/cta4j/bus/detour/model/Detour.java index a2cd487a..4a3fd3f6 100644 --- a/src/main/java/com/cta4j/bus/detour/model/Detour.java +++ b/src/main/java/com/cta4j/bus/detour/model/Detour.java @@ -9,8 +9,8 @@ /// Represents a service detour affecting one or more routes and directions within a specific time window. /// -/// **NOTE:** `dataFeed` is not well-documented by the CTA. As such, its presence here is primarily for completeness -/// and may not be populated or described correctly. +/// @apiNote `dataFeed` is not well-documented by the CTA. As such, its presence here is primarily for completeness and +/// may not be populated or described correctly. /// /// @param id the unique identifier of this detour /// @param version the version of this detour diff --git a/src/main/java/com/cta4j/bus/prediction/model/PredictionMetadata.java b/src/main/java/com/cta4j/bus/prediction/model/PredictionMetadata.java index 37c21286..4c05129b 100644 --- a/src/main/java/com/cta4j/bus/prediction/model/PredictionMetadata.java +++ b/src/main/java/com/cta4j/bus/prediction/model/PredictionMetadata.java @@ -9,8 +9,8 @@ /// Represents metadata associated with a bus arrival prediction. /// -/// **NOTE:** `gtfsSequence` and `nextBus` are not well-documented by the CTA. As such, their presence here is -/// primarily for completeness and may not be populated or described correctly. +/// @apiNote `gtfsSequence` and `nextBus` are not well-documented by the CTA. As such, their presence here is primarily +/// for completeness and may not be populated or described correctly. /// /// @param timestamp the date and time (UTC) this prediction was generated /// @param dynamicAction the [DynamicAction] affecting this prediction diff --git a/src/main/java/com/cta4j/bus/prediction/query/StopPredictionsQuery.java b/src/main/java/com/cta4j/bus/prediction/query/StopPredictionsQuery.java index 253c3992..dd7ac792 100644 --- a/src/main/java/com/cta4j/bus/prediction/query/StopPredictionsQuery.java +++ b/src/main/java/com/cta4j/bus/prediction/query/StopPredictionsQuery.java @@ -54,14 +54,11 @@ public static Builder builder(Collection stopIds) { /// A builder for `StopPredictionsQuery`. public static final class Builder { - /// The [List] of stop IDs to retrieve predictions for. private final List stopIds; - /// The optional [List] of route IDs to filter predictions by. @Nullable private List routeIds; - /// The optional maximum number of predictions to return. @Nullable private Integer maxResults; diff --git a/src/main/java/com/cta4j/bus/prediction/query/VehiclePredictionsQuery.java b/src/main/java/com/cta4j/bus/prediction/query/VehiclePredictionsQuery.java index 4aac35de..f1910a97 100644 --- a/src/main/java/com/cta4j/bus/prediction/query/VehiclePredictionsQuery.java +++ b/src/main/java/com/cta4j/bus/prediction/query/VehiclePredictionsQuery.java @@ -47,10 +47,8 @@ public static Builder builder(Collection vehicleIds) { /// Builder for `VehiclePredictionsQuery`. public static final class Builder { - /// The [List] of vehicle IDs to retrieve predictions for. private final List vehicleIds; - /// The optional maximum number of predictions to return. @Nullable private Integer maxResults; diff --git a/src/main/java/com/cta4j/bus/route/model/Route.java b/src/main/java/com/cta4j/bus/route/model/Route.java index a571e3dc..7e510cca 100644 --- a/src/main/java/com/cta4j/bus/route/model/Route.java +++ b/src/main/java/com/cta4j/bus/route/model/Route.java @@ -7,8 +7,8 @@ /// Represents a bus route. /// -/// **NOTE:** `dataFeed` is not well-documented by the CTA. As such, its presence here is primarily for completeness -/// and may not be populated or described correctly. +/// @apiNote `dataFeed` is not well-documented by the CTA. As such, its presence here is primarily for completeness and +/// may not be populated or described correctly. /// /// @param id the alphanumeric designator of this route (e.g., "22", "J14", "X9") /// @param name the common name of this route (e.g., "Clark", "Jeffery Jump", "Ashland Express") diff --git a/src/main/java/com/cta4j/bus/stop/model/Stop.java b/src/main/java/com/cta4j/bus/stop/model/Stop.java index 304c2abf..7fab9013 100644 --- a/src/main/java/com/cta4j/bus/stop/model/Stop.java +++ b/src/main/java/com/cta4j/bus/stop/model/Stop.java @@ -9,8 +9,8 @@ /// Represents a bus stop. /// -/// **NOTE:** `gtfsSequence` is not well-documented by the CTA. As such, its presence here is primarily for -/// completeness and may not be populated or described correctly. +/// @apiNote `gtfsSequence` is not well-documented by the CTA. As such, its presence here is primarily for completeness +/// and may not be populated or described correctly. /// /// @param id the unique identifier of this stop /// @param name the display name of this stop (e.g., "Clark & Addison") diff --git a/src/main/java/com/cta4j/bus/vehicle/model/VehicleMetadata.java b/src/main/java/com/cta4j/bus/vehicle/model/VehicleMetadata.java index 82eb5ea0..39408fea 100644 --- a/src/main/java/com/cta4j/bus/vehicle/model/VehicleMetadata.java +++ b/src/main/java/com/cta4j/bus/vehicle/model/VehicleMetadata.java @@ -10,9 +10,9 @@ /// Represents metadata associated with a vehicle. /// -/// **NOTE:** `dataFeed`, `stopStatus`, `timepointId`, `stopId`, `sequence`, `gtfsSequence`, `serverTimestamp`, -/// `speed`, and `block` are not well-documented by the CTA. As such, their presence here is primarily for completeness -/// and may not be populated or described correctly. +/// @apiNote `dataFeed`, `stopStatus`, `timepointId`, `stopId`, `sequence`, `gtfsSequence`, `serverTimestamp`, `speed`, +/// and `block` are not well-documented by the CTA. As such, their presence here is primarily for completeness and may +/// not be populated or described correctly. /// /// @param dataFeed the data feed from which this vehicle information was obtained, if applicable /// @param lastUpdated the date and time (UTC) this vehicle information was last updated, if applicable diff --git a/src/main/java/com/cta4j/common/exception/Cta4jException.java b/src/main/java/com/cta4j/common/exception/Cta4jException.java index 91881b51..e2d5d3ed 100644 --- a/src/main/java/com/cta4j/common/exception/Cta4jException.java +++ b/src/main/java/com/cta4j/common/exception/Cta4jException.java @@ -7,7 +7,6 @@ /// A custom exception class for handling cta4j-specific errors. @NullMarked public class Cta4jException extends RuntimeException { - /// The endpoint associated with this exception. private final String endpoint; /// Constructs a `Cta4jException`. diff --git a/src/main/java/com/cta4j/train/TrainApi.java b/src/main/java/com/cta4j/train/TrainApi.java index fd8a1147..e6341d3f 100644 --- a/src/main/java/com/cta4j/train/TrainApi.java +++ b/src/main/java/com/cta4j/train/TrainApi.java @@ -9,86 +9,66 @@ import java.util.Objects; -/** - * Primary entry point for interacting with the CTA Train Tracker API. - *

- * This interface provides grouped sub-APIs for different aspects of the Train Tracker API, such as stations, arrivals, - * train following, and locations. - *

- * Instances of {@code TrainApi} are immutable and thread-safe once built. - * Use {@link #builder(String)} to construct a configured instance. - */ +/// Primary entry point for interacting with the CTA Train Tracker API. +/// +/// This interface provides grouped sub-APIs for different aspects of the Train Tracker API, such as stations, +/// arrivals, train following, and locations. +/// +/// Instances of `TrainApi` are immutable and thread-safe once built. Use [#builder(String)] to construct a configured +/// instance. @NullMarked public interface TrainApi { - /** - * Provides access to station-related endpoints. - * - * @return the {@link StationsApi} - */ + /// Provides access to station-related endpoints. + /// + /// @return the [StationsApi] StationsApi stations(); - /** - * Provides access to arrival-related endpoints. - * - * @return the {@link ArrivalsApi} - */ + /// Provides access to arrival-related endpoints. + /// + /// @return the [ArrivalsApi] ArrivalsApi arrivals(); - /** - * Provides access to train follow-related endpoints. - * - * @return the {@link FollowApi} - */ + /// Provides access to train follow-related endpoints. + /// + /// @return the [FollowApi] FollowApi follow(); - /** - * Provides access to location-related endpoints. - * - * @return the {@link LocationsApi} - */ + /// Provides access to location-related endpoints. + /// + /// @return the [LocationsApi] LocationsApi locations(); - /** - * Builder for constructing {@link TrainApi} instances. - */ + /// Builder for constructing [TrainApi] instances. interface Builder { - /** - * Sets the API host to use for requests. - *

- * If not specified, the default CTA Train Tracker API host is used. - * - * @param host the API host - * @return this builder instance - * @throws NullPointerException if {@code host} is {@code null} - */ + /// Sets the API host to use for requests. + /// + /// If not specified, the default CTA Train Tracker API host is used. + /// + /// @param host the API host + /// @return this builder instance + /// @throws NullPointerException if `host` is `null` Builder host(String host); - /** - * Sets the URL to fetch station data from. - *

- * If not specified, the default URL for station data is used. - * - * @param stationsUrl the URL for station data - * @return this builder instance - * @throws NullPointerException if {@code stationsUrl} is {@code null} - */ + /// Sets the URL to fetch station data from. + /// + /// If not specified, the default URL for station data is used. + /// + /// @param stationsUrl the URL for station data + /// @return this builder instance + /// @throws NullPointerException if `stationsUrl` is `null` Builder stationsUrl(String stationsUrl); - /** - * Builds a configured {@link TrainApi} instance. - * - * @return a new {@link TrainApi} - */ + /// Builds a configured [TrainApi] instance. + /// + /// @return a new [TrainApi] TrainApi build(); } - /** - * Creates a new {@link Builder} for constructing a {@link TrainApi}. - * - * @param apiKey the CTA Train Tracker API key - * @return a new {@link Builder} - * @throws NullPointerException if {@code apiKey} is {@code null} - */ + /// Creates a new [Builder] for constructing a [TrainApi]. + /// + /// @param apiKey the CTA Train Tracker API key + /// @return a new [Builder] + /// @throws NullPointerException if `apiKey` is `null` static Builder builder(String apiKey) { Objects.requireNonNull(apiKey); diff --git a/src/main/java/com/cta4j/train/arrival/ArrivalsApi.java b/src/main/java/com/cta4j/train/arrival/ArrivalsApi.java index e972855d..c37dc33f 100644 --- a/src/main/java/com/cta4j/train/arrival/ArrivalsApi.java +++ b/src/main/java/com/cta4j/train/arrival/ArrivalsApi.java @@ -8,33 +8,27 @@ import java.util.List; -/** - * Provides access to arrival-related endpoints of the CTA Train Tracker API. - *

- * This API allows retrieval of arrivals by map ID or stop ID. - */ +/// Provides access to arrival-related endpoints of the CTA Train Tracker API. +/// +/// This API allows retrieval of arrivals by map ID or stop ID. @NullMarked public interface ArrivalsApi { - /** - * Retrieves arrivals by map ID. - * - * @param query the query parameters for fetching arrivals by map ID - * @return a {@link List} of {@link Arrival}s corresponding to the provided map ID, or an empty {@link List} if no - * arrivals are found - * @throws NullPointerException if {@code query} is {@code null} - * @throws Cta4jArrivalsException if the API returns an error response or the response cannot be parsed - */ + /// Retrieves arrivals by map ID. + /// + /// @param query the query parameters for fetching arrivals by map ID + /// @return a [List] of [Arrival]s corresponding to the provided map ID, or an empty [List] if no arrivals are + /// found + /// @throws NullPointerException if `query` is `null` + /// @throws Cta4jArrivalsException if the API returns an error response or the response cannot be parsed List findByMapId(MapArrivalsQuery query); - /** - * Retrieves arrivals by map ID. - * - * @param mapId the map ID - * @return a {@link List} of {@link Arrival}s corresponding to the provided map ID, or an empty {@link List} if no - * arrivals are found - * @throws NullPointerException if {@code mapId} is {@code null} - * @throws Cta4jArrivalsException if the API returns an error response or the response cannot be parsed - */ + /// Retrieves arrivals by map ID. + /// + /// @param mapId the map ID + /// @return a [List] of [Arrival]s corresponding to the provided map ID, or an empty [List] if no arrivals are + /// found + /// @throws NullPointerException if `mapId` is `null` + /// @throws Cta4jArrivalsException if the API returns an error response or the response cannot be parsed default List findByMapId(String mapId) { MapArrivalsQuery query = MapArrivalsQuery.builder(mapId) .build(); @@ -42,26 +36,22 @@ default List findByMapId(String mapId) { return this.findByMapId(query); } - /** - * Retrieves arrivals by stop ID. - * - * @param query the query parameters for fetching arrivals by stop ID - * @return a {@link List} of {@link Arrival}s corresponding to the provided stop ID, or an empty {@link List} if no - * arrivals are found - * @throws NullPointerException if {@code query} is {@code null} - * @throws Cta4jArrivalsException if the API returns an error response or the response cannot be parsed - */ + /// Retrieves arrivals by stop ID. + /// + /// @param query the query parameters for fetching arrivals by stop ID + /// @return a [List] of [Arrival]s corresponding to the provided stop ID, or an empty [List] if no arrivals are + /// found + /// @throws NullPointerException if `query` is `null` + /// @throws Cta4jArrivalsException if the API returns an error response or the response cannot be parsed List findByStopId(StopArrivalsQuery query); - /** - * Retrieves arrivals by stop ID. - * - * @param stopId the stop ID - * @return a {@link List} of {@link Arrival}s corresponding to the provided stop ID, or an empty {@link List} if no - * arrivals are found - * @throws NullPointerException if {@code stopId} is {@code null} - * @throws Cta4jArrivalsException if the API returns an error response or the response cannot be parsed - */ + /// Retrieves arrivals by stop ID. + /// + /// @param stopId the stop ID + /// @return a [List] of [Arrival]s corresponding to the provided stop ID, or an empty [List] if no arrivals are + /// found + /// @throws NullPointerException if `stopId` is `null` + /// @throws Cta4jArrivalsException if the API returns an error response or the response cannot be parsed default List findByStopId(String stopId) { StopArrivalsQuery query = StopArrivalsQuery.builder(stopId) .build(); diff --git a/src/main/java/com/cta4j/train/arrival/exception/ArrivalsErrorCode.java b/src/main/java/com/cta4j/train/arrival/exception/ArrivalsErrorCode.java index 9e58dbb4..409c2150 100644 --- a/src/main/java/com/cta4j/train/arrival/exception/ArrivalsErrorCode.java +++ b/src/main/java/com/cta4j/train/arrival/exception/ArrivalsErrorCode.java @@ -2,127 +2,79 @@ import org.jspecify.annotations.NullMarked; -/** - * Represents the error codes returned by the CTA Arrivals API. - */ +/// Represents the error codes returned by the CTA Arrivals API. @NullMarked public enum ArrivalsErrorCode { - /** - * Indicates that the request was successful and there were no errors. - */ + /// Indicates that the request was successful and there were no errors. OK(0), - /** - * Indicates that a required parameter is missing from the request. - */ + /// Indicates that a required parameter is missing from the request. MISSING_PARAMETER(100), - /** - * Indicates that the provided API key is invalid. - */ + /// Indicates that the provided API key is invalid. INVALID_API_KEY(101), - /** - * Indicates that the daily limit for API requests has been exceeded. - */ + /// Indicates that the daily limit for API requests has been exceeded. DAILY_LIMIT_EXCEEDED(102), - /** - * Indicates that the provided map ID is invalid. - */ + /// Indicates that the provided map ID is invalid. INVALID_MAPID(103), - /** - * Indicates that the provided map ID is not an integer. - */ + /// Indicates that the provided map ID is not an integer. MAPID_NOT_INTEGER(104), - /** - * Indicates that the number of map IDs provided exceeds the allowed limit (more than 4). - */ + /// Indicates that the number of map IDs provided exceeds the allowed limit (more than 4). TOO_MANY_MAPIDS(105), - /** - * Indicates that the provided route is invalid. - */ + /// Indicates that the provided route is invalid. INVALID_ROUTE(106), - /** - * Indicates that the number of routes provided exceeds the allowed limit (more than 4). - */ + /// Indicates that the number of routes provided exceeds the allowed limit (more than 4). TOO_MANY_ROUTES(107), - /** - * Indicates that the provided stop ID is invalid. - */ + /// Indicates that the provided stop ID is invalid. INVALID_STPID(108), - /** - * Indicates that the number of stop IDs provided exceeds the allowed limit (more than 4). - */ + /// Indicates that the number of stop IDs provided exceeds the allowed limit (more than 4). TOO_MANY_STPIDS(109), - /** - * Indicates that a non-integer value was specified for the maximum number of results. - */ + /// Indicates that a noninteger value was specified for the maximum number of results. INVALID_MAX(110), - /** - * Indicates that the provided maximum number of results is not a positive integer. - */ + /// Indicates that the provided maximum number of results is not a positive integer. MAX_NOT_POSITIVE(111), - /** - * Indicates that the provided stop ID is not an integer. - */ + /// Indicates that the provided stop ID is not an integer. STPID_NOT_INTEGER(112), - /** - * Indicates that the query string contains a parameter that is not recognized by the API. The supported API - * parameters are "mapid", "key", "rt", "stpid", and "max". - */ + /// Indicates that the query string contains a parameter that is not recognized by the API. The supported API + /// parameters are "mapid", "key", "rt", "stpid", and "max". INVALID_PARAMETER(500), - /** - * Indicates that the server encountered an unexpected error that prevented it from fulfilling the request. - */ + /// Indicates that the server encountered an unexpected error that prevented it from fulfilling the request. SERVER_ERROR(900), - /** - * Indicates that an unknown error occurred that does not match any of the defined error codes. - */ + /// Indicates that an unknown error occurred that does not match any of the defined error codes. UNKNOWN(-1); - /** - * The integer code associated with this error code. - */ private final int code; - /** - * Constructs an {@code ArrivalsErrorCode}. - * - * @param code the integer code associated with the error code - */ ArrivalsErrorCode(int code) { this.code = code; } - /** - * Returns the integer code associated with this error code. - * - * @return the integer code - */ + /// Returns the integer code associated with this error code. + /// + /// @return the integer code public int getCode() { return this.code; } - /** - * Returns the {@code ArrivalsErrorCode} corresponding to the given integer code. - * - * @param code the integer code to look up - * @return the corresponding {@code ArrivalsErrorCode}, or {@code UNKNOWN} if the code does not match any defined - * error code - */ + /// Returns the `ArrivalsErrorCode` corresponding to the given integer code. + /// + /// @param code the integer code to look up + /// @return the corresponding `ArrivalsErrorCode`, or `UNKNOWN` if the code does not match any defined + /// error code public static ArrivalsErrorCode fromCode(int code) { return switch (code) { case 0 -> OK; diff --git a/src/main/java/com/cta4j/train/arrival/exception/Cta4jArrivalsException.java b/src/main/java/com/cta4j/train/arrival/exception/Cta4jArrivalsException.java index bf4d72d7..b88c896d 100644 --- a/src/main/java/com/cta4j/train/arrival/exception/Cta4jArrivalsException.java +++ b/src/main/java/com/cta4j/train/arrival/exception/Cta4jArrivalsException.java @@ -5,46 +5,35 @@ import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; -/** - * A custom exception class for handling cta4j train arrival-specific errors. - */ +/// A custom exception class for handling cta4j train arrival-specific errors. @NullMarked public final class Cta4jArrivalsException extends Cta4jTrainException { - /** - * The error code associated with this exception, if available. - */ @Nullable private final ArrivalsErrorCode errorCode; - /** - * Constructs a {@code Cta4jArrivalsException}. - * - * @param message the detail message - * @param cause the cause of the exception - */ + /// Constructs a `Cta4jArrivalsException`. + /// + /// @param message the detail message + /// @param cause the cause of the exception public Cta4jArrivalsException(String message, Throwable cause) { super(message, TrainApiConstants.ARRIVALS_ENDPOINT, cause); this.errorCode = null; } - /** - * Constructs a {@code Cta4jArrivalsException}. - * - * @param message the detail message - * @param rawErrorCode the raw error code associated with the exception - */ + /// Constructs a `Cta4jArrivalsException`. + /// + /// @param message the detail message + /// @param rawErrorCode the raw error code associated with the exception public Cta4jArrivalsException(String message, int rawErrorCode) { super(message, TrainApiConstants.ARRIVALS_ENDPOINT, rawErrorCode); this.errorCode = ArrivalsErrorCode.fromCode(rawErrorCode); } - /** - * Returns the error code associated with this exception, if available. - * - * @return the error code, or {@code null} if not available - */ + /// Returns the error code associated with this exception, if available. + /// + /// @return the error code, or `null` if not available public @Nullable ArrivalsErrorCode getErrorCode() { return this.errorCode; } diff --git a/src/main/java/com/cta4j/train/arrival/query/MapArrivalsQuery.java b/src/main/java/com/cta4j/train/arrival/query/MapArrivalsQuery.java index 87ffafb7..af53c5b4 100644 --- a/src/main/java/com/cta4j/train/arrival/query/MapArrivalsQuery.java +++ b/src/main/java/com/cta4j/train/arrival/query/MapArrivalsQuery.java @@ -6,28 +6,24 @@ import java.util.Objects; -/** - * Represents a query for train arrivals at a specific map. - * - * @param mapId the ID of the map to retrieve arrivals for - * @param line the optional train line to filter arrivals by - * @param maxResults the optional maximum number of arrivals to return - */ +/// Represents a query for train arrivals at a specific map. +/// +/// @param mapId the ID of the map to retrieve arrivals for +/// @param line the optional train line to filter arrivals by +/// @param maxResults the optional maximum number of arrivals to return @NullMarked public record MapArrivalsQuery( String mapId, @Nullable TrainLine line, @Nullable Integer maxResults ) { - /** - * Constructs a {@code MapArrivalsQuery}. - * - * @param mapId the ID of the map to retrieve arrivals for - * @param line the optional train line to filter arrivals by - * @param maxResults the optional maximum number of arrivals to return - * @throws NullPointerException if {@code mapId} is {@code null} - * @throws IllegalArgumentException if {@code maxResults} is non-{@code null} and not positive - */ + /// Constructs a `MapArrivalsQuery`. + /// + /// @param mapId the ID of the map to retrieve arrivals for + /// @param line the optional train line to filter arrivals by + /// @param maxResults the optional maximum number of arrivals to return + /// @throws NullPointerException if `mapId` is `null` + /// @throws IllegalArgumentException if `maxResults` is non-`null` and not positive public MapArrivalsQuery { Objects.requireNonNull(mapId); @@ -36,68 +32,49 @@ public record MapArrivalsQuery( } } - /** - * Creates a builder for {@code MapArrivalsQuery}. - * - * @param mapId the ID of the map to retrieve arrivals for - * @return a new {@code Builder} instance - * @throws NullPointerException if {@code mapId} is {@code null} - */ + /// Creates a builder for `MapArrivalsQuery`. + /// + /// @param mapId the ID of the map to retrieve arrivals for + /// @return a new `Builder` instance + /// @throws NullPointerException if `mapId` is `null` public static Builder builder(String mapId) { return new Builder(mapId); } - /** - * A builder for {@code MapArrivalsQuery}. - */ + /// A builder for `MapArrivalsQuery`. public static final class Builder { - /** - * The ID of the map to retrieve arrivals for. - */ private final String mapId; - /** - * The optional train line to filter arrivals by. - */ @Nullable private TrainLine line; - /** - * The optional maximum number of arrivals to return. - */ @Nullable private Integer maxResults; - /** - * Constructs a {@code Builder}. - * - * @param mapId the ID of the map to retrieve arrivals for - * @throws NullPointerException if {@code mapId} is {@code null} - */ + /// Constructs a `Builder`. + /// + /// @param mapId the ID of the map to retrieve arrivals for + /// @throws NullPointerException if `mapId` is `null` public Builder(String mapId) { this.mapId = Objects.requireNonNull(mapId); } - /** - * Sets the train line to filter arrivals by. - * - * @param line the train line - * @return this {@code Builder} instance - * @throws NullPointerException if {@code line} is {@code null} - */ + /// Sets the train line to filter arrivals by. + /// + /// @param line the train line + /// @return this `Builder` instance + /// @throws NullPointerException if `line` is `null` public Builder line(TrainLine line) { this.line = Objects.requireNonNull(line); return this; } - /** - * Sets the maximum number of arrivals to return. - * - * @param maxResults the maximum number of arrivals - * @return this {@code Builder} instance - * @throws IllegalArgumentException if {@code maxResults} is not positive - */ + /// Sets the maximum number of arrivals to return. + /// + /// @param maxResults the maximum number of arrivals + /// @return this `Builder` instance + /// @throws IllegalArgumentException if `maxResults` is not positive public Builder maxResults(int maxResults) { if (maxResults <= 0) { throw new IllegalArgumentException("maxResults must be positive"); @@ -108,11 +85,9 @@ public Builder maxResults(int maxResults) { return this; } - /** - * Builds the {@code MapArrivalsQuery}. - * - * @return a new {@code MapArrivalsQuery} instance - */ + /// Builds the `MapArrivalsQuery`. + /// + /// @return a new `MapArrivalsQuery` instance public MapArrivalsQuery build() { return new MapArrivalsQuery( this.mapId, diff --git a/src/main/java/com/cta4j/train/arrival/query/StopArrivalsQuery.java b/src/main/java/com/cta4j/train/arrival/query/StopArrivalsQuery.java index 23b84051..a4526b69 100644 --- a/src/main/java/com/cta4j/train/arrival/query/StopArrivalsQuery.java +++ b/src/main/java/com/cta4j/train/arrival/query/StopArrivalsQuery.java @@ -6,28 +6,24 @@ import java.util.Objects; -/** - * Represents a query for train arrivals at a specific stop. - * - * @param stopId the ID of the stop to retrieve arrivals for - * @param line the optional train line to filter arrivals by - * @param maxResults the optional maximum number of arrivals to return - */ +/// Represents a query for train arrivals at a specific stop. +/// +/// @param stopId the ID of the stop to retrieve arrivals for +/// @param line the optional train line to filter arrivals by +/// @param maxResults the optional maximum number of arrivals to return @NullMarked public record StopArrivalsQuery( String stopId, @Nullable TrainLine line, @Nullable Integer maxResults ) { - /** - * Constructs a {@code StopArrivalsQuery}. - * - * @param stopId the ID of the stop to retrieve arrivals for - * @param line the optional train line to filter arrivals by - * @param maxResults the optional maximum number of arrivals to return - * @throws NullPointerException if {@code stopId} is {@code null} - * @throws IllegalArgumentException if {@code maxResults} is non-{@code null} and not positive - */ + /// Constructs a `StopArrivalsQuery`. + /// + /// @param stopId the ID of the stop to retrieve arrivals for + /// @param line the optional train line to filter arrivals by + /// @param maxResults the optional maximum number of arrivals to return + /// @throws NullPointerException if `stopId` is `null` + /// @throws IllegalArgumentException if `maxResults` is non-`null` and not positive public StopArrivalsQuery { Objects.requireNonNull(stopId); @@ -36,68 +32,49 @@ public record StopArrivalsQuery( } } - /** - * Creates a builder for {@code StopArrivalsQuery}. - * - * @param stopId the ID of the stop to retrieve arrivals for - * @return a new {@code Builder} instance - * @throws NullPointerException if {@code stopId} is {@code null} - */ + /// Creates a builder for `StopArrivalsQuery`. + /// + /// @param stopId the ID of the stop to retrieve arrivals for + /// @return a new `Builder` instance + /// @throws NullPointerException if `stopId` is `null` public static Builder builder(String stopId) { return new Builder(stopId); } - /** - * A builder for {@code StopArrivalsQuery}. - */ + /// A builder for `StopArrivalsQuery`. public static final class Builder { - /** - * The ID of the stop to retrieve arrivals for. - */ private final String stopId; - /** - * The optional train line to filter arrivals by. - */ @Nullable private TrainLine line; - /** - * The optional maximum number of arrivals to return. - */ @Nullable private Integer maxResults; - /** - * Constructs a {@code Builder}. - * - * @param stopId the ID of the stop to retrieve arrivals for - * @throws NullPointerException if {@code stopId} is {@code null} - */ + /// Constructs a `Builder`. + /// + /// @param stopId the ID of the stop to retrieve arrivals for + /// @throws NullPointerException if `stopId` is `null` public Builder(String stopId) { this.stopId = Objects.requireNonNull(stopId); } - /** - * Sets the train line to filter arrivals by. - * - * @param line the train line - * @return this {@code Builder} instance - * @throws NullPointerException if {@code line} is {@code null} - */ + /// Sets the train line to filter arrivals by. + /// + /// @param line the train line + /// @return this `Builder` instance + /// @throws NullPointerException if `line` is `null` public Builder line(TrainLine line) { this.line = Objects.requireNonNull(line); return this; } - /** - * Sets the maximum number of arrivals to return. - * - * @param maxResults the maximum number of arrivals - * @return this {@code Builder} instance - * @throws IllegalArgumentException if {@code maxResults} is not positive - */ + /// Sets the maximum number of arrivals to return. + /// + /// @param maxResults the maximum number of arrivals + /// @return this `Builder` instance + /// @throws IllegalArgumentException if `maxResults` is not positive public Builder maxResults(int maxResults) { if (maxResults <= 0) { throw new IllegalArgumentException("maxResults must be positive"); @@ -108,11 +85,9 @@ public Builder maxResults(int maxResults) { return this; } - /** - * Builds the {@code StopArrivalsQuery}. - * - * @return a new {@code StopArrivalsQuery} instance - */ + /// Builds the `StopArrivalsQuery`. + /// + /// @return a new `StopArrivalsQuery` instance public StopArrivalsQuery build() { return new StopArrivalsQuery( this.stopId, diff --git a/src/main/java/com/cta4j/train/common/exception/Cta4jTrainException.java b/src/main/java/com/cta4j/train/common/exception/Cta4jTrainException.java index e2a21309..b5c1c93d 100644 --- a/src/main/java/com/cta4j/train/common/exception/Cta4jTrainException.java +++ b/src/main/java/com/cta4j/train/common/exception/Cta4jTrainException.java @@ -4,63 +4,50 @@ import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; -/** - * A custom exception class for handling cta4j train-specific errors. - */ +/// A custom exception class for handling cta4j train-specific errors. @NullMarked public class Cta4jTrainException extends Cta4jException { - /** - * The raw error code associated with this exception, if available. - */ @Nullable private final Integer rawErrorCode; - /** - * Constructs a {@code Cta4jTrainException}. - * - * @param message the detail message - * @param endpoint the endpoint associated with the exception - * @throws NullPointerException if {@code endpoint} is {@code null} - */ + /// Constructs a `Cta4jTrainException`. + /// + /// @param message the detail message + /// @param endpoint the endpoint associated with the exception + /// @throws NullPointerException if `endpoint` is `null` public Cta4jTrainException(String message, String endpoint) { super(message, endpoint); this.rawErrorCode = null; } - /** - * Constructs a {@code Cta4jTrainException}. - * - * @param message the detail message - * @param endpoint the endpoint associated with the exception - * @param cause the cause of the exception - * @throws NullPointerException if {@code endpoint} is {@code null} - */ + /// Constructs a `Cta4jTrainException`. + /// + /// @param message the detail message + /// @param endpoint the endpoint associated with the exception + /// @param cause the cause of the exception + /// @throws NullPointerException if `endpoint` is `null` public Cta4jTrainException(String message, String endpoint, Throwable cause) { super(message, endpoint, cause); this.rawErrorCode = null; } - /** - * Constructs a {@code Cta4jTrainException} with a raw error code. - * - * @param message the detail message - * @param endpoint the endpoint associated with the exception - * @param rawErrorCode the raw error code associated with the exception - * @throws NullPointerException if {@code endpoint} is {@code null} - */ + /// Constructs a `Cta4jTrainException` with a raw error code. + /// + /// @param message the detail message + /// @param endpoint the endpoint associated with the exception + /// @param rawErrorCode the raw error code associated with the exception + /// @throws NullPointerException if `endpoint` is `null` public Cta4jTrainException(String message, String endpoint, int rawErrorCode) { super(message, endpoint); this.rawErrorCode = rawErrorCode; } - /** - * Returns the raw error code associated with this exception, if available. - * - * @return the raw error code, or {@code null} if not available - */ + /// Returns the raw error code associated with this exception, if available. + /// + /// @return the raw error code, or `null` if not available public @Nullable Integer getRawErrorCode() { return this.rawErrorCode; } diff --git a/src/main/java/com/cta4j/train/common/model/Arrival.java b/src/main/java/com/cta4j/train/common/model/Arrival.java index 3b4eab09..f06252af 100644 --- a/src/main/java/com/cta4j/train/common/model/Arrival.java +++ b/src/main/java/com/cta4j/train/common/model/Arrival.java @@ -5,25 +5,23 @@ import java.time.Instant; import java.util.Objects; -/** - * Represents a train arrival. - * - * @param stationId the unique identifier of the station for which this arrival was generated - * @param stationName the display name of the station for which this arrival was generated - * @param stopId the unique identifier of the stop for which this arrival was generated - * @param stopDescription the display name of the stop for which this arrival was generated - * @param line the train line associated with this arrival - * @param destinationStationId the unique identifier of the destination station for this arrival - * @param destinationName the display name of the destination station for this arrival - * @param predictionTime the date and time (UTC) this arrival was generated - * @param arrivalTime the date and time (UTC) of a train’s arrival or departure to the stop associated with this - * arrival - * @param approaching whether the train associated with this arrival is currently approaching the stop - * @param scheduled whether this arrival is based on a scheduled time rather than a real-time prediction - * @param delayed whether the train associated with this arrival is currently delayed - * @param fault whether the train associated with this arrival is currently experiencing a fault - * @param metadata the metadata associated with this arrival - */ +/// Represents a train arrival. +/// +/// @param stationId the unique identifier of the station for which this arrival was generated +/// @param stationName the display name of the station for which this arrival was generated +/// @param stopId the unique identifier of the stop for which this arrival was generated +/// @param stopDescription the display name of the stop for which this arrival was generated +/// @param line the train line associated with this arrival +/// @param destinationStationId the unique identifier of the destination station for this arrival +/// @param destinationName the display name of the destination station for this arrival +/// @param predictionTime the date and time (UTC) this arrival was generated +/// @param arrivalTime the date and time (UTC) of a train’s arrival or departure to the stop associated with this +/// arrival +/// @param approaching whether the train associated with this arrival is currently approaching the stop +/// @param scheduled whether this arrival is based on a scheduled time rather than a real-time prediction +/// @param delayed whether the train associated with this arrival is currently delayed +/// @param fault whether the train associated with this arrival is currently experiencing a fault +/// @param metadata the metadata associated with this arrival @NullMarked public record Arrival( String stationId, @@ -41,28 +39,25 @@ public record Arrival( boolean fault, ArrivalMetadata metadata ) { - /** - * Constructs an {@code Arrival}. - * - * @param stationId the unique identifier of the station for which the arrival was generated - * @param stationName the display name of the station for which the arrival was generated - * @param stopId the unique identifier of the stop for which the arrival was generated - * @param stopDescription the display name of the stop for which the arrival was generated - * @param line the train line associated with the arrival - * @param destinationStationId the unique identifier of the destination station for the arrival - * @param destinationName the display name of the destination station for the arrival - * @param predictionTime the date and time (UTC) the arrival was generated - * @param arrivalTime the date and time (UTC) of a train’s arrival or departure to the stop associated with the - * arrival - * @param approaching whether the train associated with the arrival is currently approaching the stop - * @param scheduled whether the arrival is based on a scheduled time rather than a real-time prediction - * @param delayed whether the train associated with the arrival is currently delayed - * @param fault whether the train associated with the arrival is currently experiencing a fault - * @param metadata the metadata associated with the arrival - * @throws NullPointerException if {@code stationId}, {@code stationName}, {@code stopId}, {@code stopDescription}, - * {@code line}, {@code destinationStationId}, {@code destinationName}, {@code predictionTime}, - * {@code arrivalTime}, or {@code metadata} is {@code null} - */ + /// Constructs an `Arrival`. + /// + /// @param stationId the unique identifier of the station for which the arrival was generated + /// @param stationName the display name of the station for which the arrival was generated + /// @param stopId the unique identifier of the stop for which the arrival was generated + /// @param stopDescription the display name of the stop for which the arrival was generated + /// @param line the train line associated with the arrival + /// @param destinationStationId the unique identifier of the destination station for the arrival + /// @param destinationName the display name of the destination station for the arrival + /// @param predictionTime the date and time (UTC) the arrival was generated + /// @param arrivalTime the date and time (UTC) of a train’s arrival or departure to the stop associated with the + /// arrival + /// @param approaching whether the train associated with the arrival is currently approaching the stop + /// @param scheduled whether the arrival is based on a scheduled time rather than a real-time prediction + /// @param delayed whether the train associated with the arrival is currently delayed + /// @param fault whether the train associated with the arrival is currently experiencing a fault + /// @param metadata the metadata associated with the arrival + /// @throws NullPointerException if `stationId`, `stationName`, `stopId`, `stopDescription`, `line`, + /// `destinationStationId`, `destinationName`, `predictionTime`, `arrivalTime`, or `metadata` is `null` public Arrival { Objects.requireNonNull(stationId); Objects.requireNonNull(stationName); diff --git a/src/main/java/com/cta4j/train/common/model/ArrivalMetadata.java b/src/main/java/com/cta4j/train/common/model/ArrivalMetadata.java index ecd97fcc..413f6d40 100644 --- a/src/main/java/com/cta4j/train/common/model/ArrivalMetadata.java +++ b/src/main/java/com/cta4j/train/common/model/ArrivalMetadata.java @@ -6,19 +6,15 @@ import java.util.Objects; -/** - * Represents metadata associated with a train arrival. - * - *

- * NOTE: {@code flags} is not well-documented by the CTA. As such, its presence here is primarily for - * completeness and may not be populated or described correctly. - *

- * - * @param runNumber the run number of the train associated with this arrival - * @param direction the direction of travel of the train associated with this arrival - * @param coordinates the coordinates of the train associated with this arrival, if applicable - * @param flags the flags associated with this arrival, if applicable - */ +/// Represents metadata associated with a train arrival. +/// +/// @apiNote `flags` is not well-documented by the CTA. As such, its presence here is primarily for completeness and +/// may not be populated or described correctly. +/// +/// @param runNumber the run number of the train associated with this arrival +/// @param direction the direction of travel of the train associated with this arrival +/// @param coordinates the coordinates of the train associated with this arrival, if applicable +/// @param flags the flags associated with this arrival, if applicable @NullMarked public record ArrivalMetadata( String runNumber, @@ -26,15 +22,13 @@ public record ArrivalMetadata( @Nullable Coordinates coordinates, @Nullable String flags ) { - /** - * Constructs an {@code ArrivalMetadata}. - * - * @param runNumber the run number of the train associated with the arrival - * @param direction the direction of travel of the train associated with the arrival - * @param coordinates the coordinates of the train associated with the arrival, if applicable - * @param flags the flags associated with the arrival, if applicable - * @throws NullPointerException if {@code runNumber} or {@code direction} is {@code null} - */ + /// Constructs an `ArrivalMetadata`. + /// + /// @param runNumber the run number of the train associated with the arrival + /// @param direction the direction of travel of the train associated with the arrival + /// @param coordinates the coordinates of the train associated with the arrival, if applicable + /// @param flags the flags associated with the arrival, if applicable + /// @throws NullPointerException if `runNumber` or `direction` is `null` public ArrivalMetadata { Objects.requireNonNull(runNumber); Objects.requireNonNull(direction); diff --git a/src/main/java/com/cta4j/train/common/model/TrainDirection.java b/src/main/java/com/cta4j/train/common/model/TrainDirection.java index f299505e..479896f8 100644 --- a/src/main/java/com/cta4j/train/common/model/TrainDirection.java +++ b/src/main/java/com/cta4j/train/common/model/TrainDirection.java @@ -2,56 +2,37 @@ import org.jspecify.annotations.NullMarked; -/** - * Represents the operational direction of a train. - * - *

- * NOTE: This direction is operational in nature and does not necessarily reflect the physical direction of the - * train at its current location. It loosely translates to a northbound or southbound direction, though this may not be - * intuitive for all lines. - */ +/// Represents the operational direction of a train. +/// +/// @apiNote This direction is operational in nature and does not necessarily reflect the physical direction of the +/// train at its current location. It loosely translates to a northbound or southbound direction, though this may not +/// be intuitive for all lines. @NullMarked public enum TrainDirection { - /** - * Indicates a northbound operational direction (CTA direction code 1). - */ + /// Indicates a northbound operational direction (CTA direction code 1). NORTHBOUND(1), - /** - * Indicates a southbound operational direction (CTA direction code 5). - */ + /// Indicates a southbound operational direction (CTA direction code 5). SOUTHBOUND(5); - /** - * The CTA direction code associated with this train direction. - */ private final int code; - /** - * Constructs a {@code TrainDirection}. - * - * @param code the CTA direction code associated with this train direction - */ TrainDirection(int code) { this.code = code; } - /** - * Gets the CTA direction code associated with this direction. - * - * @return the CTA direction code - */ + /// Gets the CTA direction code associated with this direction. + /// + /// @return the CTA direction code public int getCode() { return this.code; } - /** - * Returns the {@code TrainDirection} corresponding to the given code. - * - * @param code the CTA direction code (1 for northbound, 5 for southbound) - * @return the corresponding {@code TrainDirection} - * @throws IllegalArgumentException if the code does not correspond to any known train direction - */ + /// Returns the `TrainDirection` corresponding to the given code. + /// + /// @param code the CTA direction code (1 for northbound, 5 for southbound) + /// @return the corresponding `TrainDirection` + /// @throws IllegalArgumentException if the code does not correspond to any known train direction public static TrainDirection fromCode(int code) { return switch (code) { case 1 -> NORTHBOUND; diff --git a/src/main/java/com/cta4j/train/common/model/TrainLine.java b/src/main/java/com/cta4j/train/common/model/TrainLine.java index 483bc8c8..aa9c3cda 100644 --- a/src/main/java/com/cta4j/train/common/model/TrainLine.java +++ b/src/main/java/com/cta4j/train/common/model/TrainLine.java @@ -4,98 +4,60 @@ import java.util.Objects; -/** - * Represents a train line. - */ +/// Represents a train line. @NullMarked public enum TrainLine { - /** - * Indicates the Red Line. - */ + /// Indicates the Red Line. RED("Red", "#C60C30"), - /** - * Indicates the Blue Line. - */ + /// Indicates the Blue Line. BLUE("Blue", "#00A1DE"), - /** - * Indicates the Brown Line. - */ + /// Indicates the Brown Line. BROWN("Brn", "#62361B"), - /** - * Indicates the Green Line. - */ + /// Indicates the Green Line. GREEN("G", "#009B3A"), - /** - * Indicates the Orange Line. - */ + /// Indicates the Orange Line. ORANGE("Org", "#F9461C"), - /** - * Indicates the Purple Line. - */ + /// Indicates the Purple Line. PURPLE("P", "#522398"), - /** - * Indicates the Pink Line. - */ + /// Indicates the Pink Line. PINK("Pink", "#E27EA6"), - /** - * Indicates the Yellow Line. - */ + /// Indicates the Yellow Line. YELLOW("Y", "#F9E300"); - /** - * The CTA code for this train line. - */ private final String code; - - /** - * The hex color code of this train line. - */ private final String colorHex; - /** - * Constructs a {@code TrainLine}. - * - * @param code the CTA code of the train line - * @param colorHex the hex color code of the train line - * @throws NullPointerException if {@code code} or {@code colorHex} is {@code null} - */ TrainLine(String code, String colorHex) { this.code = Objects.requireNonNull(code); this.colorHex = Objects.requireNonNull(colorHex); } - /** - * Gets the CTA code for this train line. - * - * @return the CTA code - */ + /// Gets the CTA code for this train line. + /// + /// @return the CTA code public String getCode() { return this.code; } - /** - * Gets the hex color code of this train line. - * - * @return the hex color code - */ + /// Gets the hex color code of this train line. + /// + /// @return the hex color code public String getColorHex() { return this.colorHex; } - /** - * Returns the {@code TrainLine} corresponding to the given code. - * - * @param code the CTA code of the train line (case-insensitive, may include "LINE" suffix) - * @return the corresponding {@code TrainLine} - * @throws IllegalArgumentException if the code does not correspond to any known train line - */ + /// Returns the `TrainLine` corresponding to the given code. + /// + /// @param code the CTA code of the train line (case-insensitive, may include "LINE" suffix) + /// @return the corresponding `TrainLine` + /// @throws IllegalArgumentException if the code does not correspond to any known train line public static TrainLine fromCode(String code) { Objects.requireNonNull(code); diff --git a/src/main/java/com/cta4j/train/follow/FollowApi.java b/src/main/java/com/cta4j/train/follow/FollowApi.java index a841db2e..aae1b0f6 100644 --- a/src/main/java/com/cta4j/train/follow/FollowApi.java +++ b/src/main/java/com/cta4j/train/follow/FollowApi.java @@ -6,21 +6,17 @@ import java.util.Optional; -/** - * Provides access to follow-related endpoints of the CTA Train Tracker API. - *

- * This API allows retrieval of information about a specific train run. - */ +/// Provides access to follow-related endpoints of the CTA Train Tracker API. +/// +/// This API allows retrieval of information about a specific train run. @NullMarked public interface FollowApi { - /** - * Retrieves a train by its run number. - * - * @param run the run number of the train - * @return an {@link Optional} containing the {@link FollowTrain} if found, or an empty {@link Optional} if no - * train is found for the given run number - * @throws NullPointerException if {@code run} is {@code null} - * @throws Cta4jFollowException if the API returns an error response or the response cannot be parsed - */ + /// Retrieves a train by its run number. + /// + /// @param run the run number of the train + /// @return an [Optional] containing the [FollowTrain] if found, or an empty [Optional] if no train is found for + /// the given run number + /// @throws NullPointerException if `run` is `null` + /// @throws Cta4jFollowException if the API returns an error response or the response cannot be parsed Optional findByRun(String run); } diff --git a/src/main/java/com/cta4j/train/follow/exception/Cta4jFollowException.java b/src/main/java/com/cta4j/train/follow/exception/Cta4jFollowException.java index d38fde70..fc09b101 100644 --- a/src/main/java/com/cta4j/train/follow/exception/Cta4jFollowException.java +++ b/src/main/java/com/cta4j/train/follow/exception/Cta4jFollowException.java @@ -5,46 +5,35 @@ import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; -/** - * A custom exception class for handling cta4j train follow-specific errors. - */ +/// A custom exception class for handling cta4j train follow-specific errors. @NullMarked public final class Cta4jFollowException extends Cta4jTrainException { - /** - * The error code associated with this exception, if available. - */ @Nullable private final FollowErrorCode errorCode; - /** - * Constructs a {@code Cta4jFollowException}. - * - * @param message the detail message - * @param cause the cause of the exception - */ + /// Constructs a `Cta4jFollowException`. + /// + /// @param message the detail message + /// @param cause the cause of the exception public Cta4jFollowException(String message, Throwable cause) { super(message, TrainApiConstants.FOLLOW_ENDPOINT, cause); this.errorCode = null; } - /** - * Constructs a {@code Cta4jFollowException}. - * - * @param message the detail message - * @param rawErrorCode the raw error code associated with the exception - */ + /// Constructs a `Cta4jFollowException`. + /// + /// @param message the detail message + /// @param rawErrorCode the raw error code associated with the exception public Cta4jFollowException(String message, int rawErrorCode) { super(message, TrainApiConstants.FOLLOW_ENDPOINT, rawErrorCode); this.errorCode = FollowErrorCode.fromCode(rawErrorCode); } - /** - * Returns the error code associated with this exception, if available. - * - * @return the error code, or {@code null} if not available - */ + /// Returns the error code associated with this exception, if available. + /// + /// @return the error code, or `null` if not available public @Nullable FollowErrorCode getErrorCode() { return this.errorCode; } diff --git a/src/main/java/com/cta4j/train/follow/exception/FollowErrorCode.java b/src/main/java/com/cta4j/train/follow/exception/FollowErrorCode.java index c4ee699d..8201d946 100644 --- a/src/main/java/com/cta4j/train/follow/exception/FollowErrorCode.java +++ b/src/main/java/com/cta4j/train/follow/exception/FollowErrorCode.java @@ -2,88 +2,56 @@ import org.jspecify.annotations.NullMarked; -/** - * Represents the error codes returned by the CTA Follow API. - */ +/// Represents the error codes returned by the CTA Follow API. @NullMarked public enum FollowErrorCode { - /** - * Indicates that the request was successful and there were no errors. - */ + /// Indicates that the request was successful and there were no errors. OK(0), - /** - * Indicates that a required parameter is missing from the request. - */ + /// Indicates that a required parameter is missing from the request. MISSING_PARAMETER(100), - /** - * Indicates that the provided API key is invalid. - */ + /// Indicates that the provided API key is invalid. INVALID_API_KEY(101), - /** - * Indicates that the daily limit for API requests has been exceeded. - */ + /// Indicates that the daily limit for API requests has been exceeded. DAILY_LIMIT_EXCEEDED(102), - /** - * Indicates that the query string contains a parameter that is not recognized by the API. The supported API - * parameters are "runnumber" and "key". - */ + /// Indicates that the query string contains a parameter that is not recognized by the API. The supported API + /// parameters are "runnumber" and "key". INVALID_PARAMETER(500), - /** - * Indicates that the specified run number does not correspond to any known train run. - */ + /// Indicates that the specified run number does not correspond to any known train run. RUN_NOT_FOUND(501), - /** - * Indicates that the specified train run exists, but has an unexpected exit station ID that prevents the API from - * reliably determining which predictions to report. - */ + /// Indicates that the specified train run exists, but has an unexpected exit station ID that prevents the API from + /// reliably determining which predictions to report. UNABLE_TO_DETERMINE_STOPS(502), - /** - * Indicates that the specified train run exists, but none of its available predictions are for active stations. - */ + /// Indicates that the specified train run exists, but none of its available predictions are for active stations. UNABLE_TO_FIND_PREDICTIONS(503), - /** - * Indicates that an unknown error occurred that does not match any of the defined error codes. - */ + /// Indicates that an unknown error occurred that does not match any of the defined error codes. UNKNOWN(-1); - /** - * The integer code associated with this error code. - */ private final int code; - /** - * Constructs a {@code FollowErrorCode}. - * - * @param code the integer code associated with the error code - */ FollowErrorCode(int code) { this.code = code; } - /** - * Returns the integer code associated with this error code. - * - * @return the integer code - */ + /// Returns the integer code associated with this error code. + /// + /// @return the integer code public int getCode() { return this.code; } - /** - * Returns the {@code FollowErrorCode} corresponding to the given integer code. - * - * @param code the integer code to look up - * @return the corresponding {@code FollowErrorCode}, or {@code UNKNOWN} if the code does not match any defined - * error code - */ + /// Returns the `FollowErrorCode` corresponding to the given integer code. + /// + /// @param code the integer code to look up + /// @return the corresponding `FollowErrorCode`, or `UNKNOWN` if the code does not match any defined + /// error code public static FollowErrorCode fromCode(int code) { return switch (code) { case 0 -> OK; diff --git a/src/main/java/com/cta4j/train/follow/model/FollowTrain.java b/src/main/java/com/cta4j/train/follow/model/FollowTrain.java index 155a2b83..b754acf8 100644 --- a/src/main/java/com/cta4j/train/follow/model/FollowTrain.java +++ b/src/main/java/com/cta4j/train/follow/model/FollowTrain.java @@ -8,25 +8,20 @@ import java.util.List; import java.util.Objects; -/** - * Represents a response from the "follow" endpoint of the CTA Train Tracker API. - * - * @param coordinates the current coordinates of this train being followed - * @param arrivals the {@link List} of {@link Arrival}s for this train being followed - */ +/// Represents a response from the "follow" endpoint of the CTA Train Tracker API. +/// +/// @param coordinates the current coordinates of this train being followed +/// @param arrivals the [List] of [Arrival]s for this train being followed @NullMarked public record FollowTrain( @Nullable Coordinates coordinates, List arrivals ) { - /** - * Constructs a {@code FollowTrain}. - * - * @param coordinates the current coordinates of the train being followed - * @param arrivals the {@link List} of {@link Arrival}s for the train being followed - * @throws NullPointerException if {@code arrivals} is {@code null}, or if {@code arrivals} contains {@code null} - * elements - */ + /// Constructs a `FollowTrain`. + /// + /// @param coordinates the current coordinates of the train being followed + /// @param arrivals the [List] of [Arrival]s for the train being followed + /// @throws NullPointerException if `arrivals` is `null`, or if `arrivals` contains `null` elements public FollowTrain { Objects.requireNonNull(arrivals); diff --git a/src/main/java/com/cta4j/train/location/LocationsApi.java b/src/main/java/com/cta4j/train/location/LocationsApi.java index 541fad13..00a42134 100644 --- a/src/main/java/com/cta4j/train/location/LocationsApi.java +++ b/src/main/java/com/cta4j/train/location/LocationsApi.java @@ -7,44 +7,35 @@ import java.util.List; -/** - * Provides access to location-related endpoints of the CTA Train Tracker API. - *

- * This API allows retrieval of train locations by line. - */ +/// Provides access to location-related endpoints of the CTA Train Tracker API. +/// +/// This API allows retrieval of train locations by line. @NullMarked public interface LocationsApi { - /** - * Retrieves train locations for all lines. - * - * @return a {@link List} of {@link TrainLocations} for all lines, or an empty {@link List} if no train locations - * are found - * @throws Cta4jLocationsException if the API returns an error response or the response cannot be parsed - */ + /// Retrieves train locations for all lines. + /// + /// @return a [List] of [TrainLocations] for all lines, or an empty [List] if no train locations are found + /// @throws Cta4jLocationsException if the API returns an error response or the response cannot be parsed default List list() { return findByLines(List.of(TrainLine.values())); } - /** - * Retrieves train locations for the specified lines. - * - * @param lines a {@link List} of {@link TrainLine}s to filter the train locations by - * @return a {@link List} of {@link TrainLocations} corresponding to the provided lines, or an empty {@link List} - * if no train locations are found for the specified lines - * @throws NullPointerException if {@code lines} is {@code null} or contains {@code null} elements - * @throws Cta4jLocationsException if the API returns an error response or the response cannot be parsed - */ + /// Retrieves train locations for the specified lines. + /// + /// @param lines a [List] of [TrainLine]s to filter the train locations by + /// @return a [List] of [TrainLocations] corresponding to the provided lines, or an empty [List] if no train + /// locations are found for the specified lines + /// @throws NullPointerException if `lines` is `null` or contains `null` elements + /// @throws Cta4jLocationsException if the API returns an error response or the response cannot be parsed List findByLines(List lines); - /** - * Retrieves train locations for the specified line. - * - * @param line the {@link TrainLine} to filter the train locations by - * @return a {@link List} of {@link TrainLocations} corresponding to the provided line, or an empty {@link List} if - * no train locations are found for the specified line - * @throws NullPointerException if {@code line} is {@code null} - * @throws Cta4jLocationsException if the API returns an error response or the response cannot be parsed - */ + /// Retrieves train locations for the specified line. + /// + /// @param line the [TrainLine] to filter the train locations by + /// @return a [List] of [TrainLocations] corresponding to the provided line, or an empty [List] if no train + /// locations are found for the specified line + /// @throws NullPointerException if `line` is `null` + /// @throws Cta4jLocationsException if the API returns an error response or the response cannot be parsed default List findByLine(TrainLine line) { return findByLines(List.of(line)); } diff --git a/src/main/java/com/cta4j/train/location/exception/Cta4jLocationsException.java b/src/main/java/com/cta4j/train/location/exception/Cta4jLocationsException.java index 448d0dd8..deb033d6 100644 --- a/src/main/java/com/cta4j/train/location/exception/Cta4jLocationsException.java +++ b/src/main/java/com/cta4j/train/location/exception/Cta4jLocationsException.java @@ -5,46 +5,35 @@ import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; -/** - * A custom exception class for handling cta4j train location-specific errors. - */ +/// A custom exception class for handling cta4j train location-specific errors. @NullMarked public final class Cta4jLocationsException extends Cta4jTrainException { - /** - * The error code associated with this exception, if available. - */ @Nullable private final LocationsErrorCode errorCode; - /** - * Constructs a {@code Cta4jLocationsException}. - * - * @param message the detail message - * @param cause the cause of the exception - */ + /// Constructs a `Cta4jLocationsException`. + /// + /// @param message the detail message + /// @param cause the cause of the exception public Cta4jLocationsException(String message, Throwable cause) { super(message, TrainApiConstants.POSITIONS_ENDPOINT, cause); this.errorCode = null; } - /** - * Constructs a {@code Cta4jLocationsException}. - * - * @param message the detail message - * @param rawErrorCode the raw error code associated with the exception - */ + /// Constructs a `Cta4jLocationsException`. + /// + /// @param message the detail message + /// @param rawErrorCode the raw error code associated with the exception public Cta4jLocationsException(String message, int rawErrorCode) { super(message, TrainApiConstants.POSITIONS_ENDPOINT, rawErrorCode); this.errorCode = LocationsErrorCode.fromCode(rawErrorCode); } - /** - * Returns the error code associated with this exception, if available. - * - * @return the error code, or {@code null} if not available - */ + /// Returns the error code associated with this exception, if available. + /// + /// @return the error code, or `null` if not available public @Nullable LocationsErrorCode getErrorCode() { return this.errorCode; } diff --git a/src/main/java/com/cta4j/train/location/exception/LocationsErrorCode.java b/src/main/java/com/cta4j/train/location/exception/LocationsErrorCode.java index 70c0518f..2ad25cd2 100644 --- a/src/main/java/com/cta4j/train/location/exception/LocationsErrorCode.java +++ b/src/main/java/com/cta4j/train/location/exception/LocationsErrorCode.java @@ -2,83 +2,53 @@ import org.jspecify.annotations.NullMarked; -/** - * Represents the error codes returned by the CTA Location API. - */ +/// Represents the error codes returned by the CTA Location API. @NullMarked public enum LocationsErrorCode { - /** - * Indicates that the request was successful and there were no errors. - */ + /// Indicates that the request was successful and there were no errors. OK(0), - /** - * Indicates that a required parameter is missing from the request. - */ + /// Indicates that a required parameter is missing from the request. MISSING_PARAMETER(100), - /** - * Indicates that the provided API key is invalid. - */ + /// Indicates that the provided API key is invalid. INVALID_API_KEY(101), - /** - * Indicates that the daily limit for API requests has been exceeded. - */ + /// Indicates that the daily limit for API requests has been exceeded. DAILY_LIMIT_EXCEEDED(102), - /** - * Indicates that the specified route is not valid or does not exist in the CTA system. Valid route identifiers are - * red, blue, brn, g, org, p, pink, and y. The route identifiers are case-insensitive. - */ + /// Indicates that the specified route is not valid or does not exist in the CTA system. Valid route identifiers + /// are red, blue, brn, g, org, p, pink, and y. The route identifiers are case-insensitive. INVALID_ROUTE(106), - /** - * Indicates that the number of routes provided exceeds the allowed limit (more than 8). - */ + /// Indicates that the number of routes provided exceeds the allowed limit (more than 8). TOO_MANY_ROUTES(107), - /** - * Indicates that the query string contains a parameter that is not recognized by the API. The supported API - * parameters are "rt" and "key". - */ + /// Indicates that the query string contains a parameter that is not recognized by the API. The supported API + /// parameters are "rt" and "key". INVALID_PARAMETER(500), - /** - * Indicates that an unknown error occurred that does not match any of the defined error codes. - */ + /// Indicates that an unknown error occurred that does not match any of the defined error codes. UNKNOWN(-1); - /** - * The integer code associated with this error code. - */ private final int code; - /** - * Constructs a {@code LocationsErrorCode}. - * - * @param code the integer code associated with the error code - */ LocationsErrorCode(int code) { this.code = code; } - /** - * Returns the integer code associated with this error code. - * - * @return the integer code - */ + /// Returns the integer code associated with this error code. + /// + /// @return the integer code public int getCode() { return this.code; } - /** - * Returns the {@code LocationsErrorCode} corresponding to the given integer code. - * - * @param code the integer code to look up - * @return the corresponding {@code LocationsErrorCode}, or {@code UNKNOWN} if the code does not match any defined - * error code - */ + /// Returns the `LocationsErrorCode` corresponding to the given integer code. + /// + /// @param code the integer code to look up + /// @return the corresponding `LocationsErrorCode`, or `UNKNOWN` if the code does not match any defined + /// error code public static LocationsErrorCode fromCode(int code) { return switch (code) { case 0 -> OK; diff --git a/src/main/java/com/cta4j/train/location/model/LocationTrain.java b/src/main/java/com/cta4j/train/location/model/LocationTrain.java index d4e950f6..97c68a96 100644 --- a/src/main/java/com/cta4j/train/location/model/LocationTrain.java +++ b/src/main/java/com/cta4j/train/location/model/LocationTrain.java @@ -8,28 +8,24 @@ import java.time.Instant; import java.util.Objects; -/** - * Represents the location of a train on a route. - * - *

- * NOTE: {@code flags} is not well-documented by the CTA. As such, its presence here is primarily for - * completeness and may not be populated or described correctly. - *

- * - * @param run the run number of this train - * @param destinationStationId the unique identifier of the destination station for this train - * @param destinationName the display name of the destination station for this train - * @param direction the direction of travel of this train - * @param nextStationId the unique identifier of the next station for this train - * @param nextStopId the unique identifier of the next stop for this train - * @param nextStationName the display name of the next station for this train - * @param predictionTime the date and time (UTC) this location was generated - * @param arrivalTime the date and time (UTC) of this train's arrival at the next stop - * @param approaching whether this train is currently approaching the next stop - * @param delayed whether this train is currently delayed - * @param flags the flags associated with this train, if applicable - * @param coordinates the current coordinates of this train - */ +/// Represents the location of a train on a route. +/// +/// @apiNote `flags` is not well-documented by the CTA. As such, its presence here is primarily for completeness and +/// may not be populated or described correctly +/// +/// @param run the run number of this train +/// @param destinationStationId the unique identifier of the destination station for this train +/// @param destinationName the display name of the destination station for this train +/// @param direction the direction of travel of this train +/// @param nextStationId the unique identifier of the next station for this train +/// @param nextStopId the unique identifier of the next stop for this train +/// @param nextStationName the display name of the next station for this train +/// @param predictionTime the date and time (UTC) this location was generated +/// @param arrivalTime the date and time (UTC) of this train's arrival at the next stop +/// @param approaching whether this train is currently approaching the next stop +/// @param delayed whether this train is currently delayed +/// @param flags the flags associated with this train, if applicable +/// @param coordinates the current coordinates of this train @NullMarked public record LocationTrain( String run, @@ -46,26 +42,23 @@ public record LocationTrain( @Nullable String flags, Coordinates coordinates ) { - /** - * Constructs a {@code LocationTrain}. - * - * @param run the run number of the train - * @param destinationStationId the unique identifier of the destination station for the train - * @param destinationName the display name of the destination station for the train - * @param direction the direction of travel of the train - * @param nextStationId the unique identifier of the next station for the train - * @param nextStopId the unique identifier of the next stop for the train - * @param nextStationName the display name of the next station for the train - * @param predictionTime the date and time (UTC) the location was generated - * @param arrivalTime the date and time (UTC) of the train's arrival at the next stop - * @param approaching whether the train is currently approaching the next stop - * @param delayed whether the train is currently delayed - * @param flags the flags associated with the train, if applicable - * @param coordinates the current coordinates of the train - * @throws NullPointerException if {@code run}, {@code destinationStationId}, {@code destinationName}, - * {@code direction}, {@code nextStationId}, {@code nextStopId}, {@code nextStationName}, {@code predictionTime}, - * {@code arrivalTime}, or {@code coordinates} is {@code null} - */ + /// Constructs a `LocationTrain`. + /// + /// @param run the run number of the train + /// @param destinationStationId the unique identifier of the destination station for the train + /// @param destinationName the display name of the destination station for the train + /// @param direction the direction of travel of the train + /// @param nextStationId the unique identifier of the next station for the train + /// @param nextStopId the unique identifier of the next stop for the train + /// @param nextStationName the display name of the next station for the train + /// @param predictionTime the date and time (UTC) the location was generated + /// @param arrivalTime the date and time (UTC) of the train's arrival at the next stop + /// @param approaching whether the train is currently approaching the next stop + /// @param delayed whether the train is currently delayed + /// @param flags the flags associated with the train, if applicable + /// @param coordinates the current coordinates of the train + /// @throws NullPointerException if `run`, `destinationStationId`, `destinationName`, `direction`, `nextStationId`, + /// `nextStopId`, `nextStationName`, `predictionTime`, `arrivalTime`, or `coordinates` is `null` public LocationTrain { Objects.requireNonNull(run); Objects.requireNonNull(destinationStationId); diff --git a/src/main/java/com/cta4j/train/location/model/TrainLocations.java b/src/main/java/com/cta4j/train/location/model/TrainLocations.java index 5629cf27..cf23c9da 100644 --- a/src/main/java/com/cta4j/train/location/model/TrainLocations.java +++ b/src/main/java/com/cta4j/train/location/model/TrainLocations.java @@ -6,25 +6,20 @@ import java.util.List; import java.util.Objects; -/** - * Represents the locations of all trains on a route. - * - * @param line the train line associated with these locations - * @param trains the {@link List} of {@link LocationTrain}s for this train line - */ +/// Represents the locations of all trains on a route. +/// +/// @param line the train line associated with these locations +/// @param trains the [List] of [LocationTrain]s for this train line @NullMarked public record TrainLocations( TrainLine line, List trains ) { - /** - * Constructs a {@code TrainLocations}. - * - * @param line the train line associated with the locations - * @param trains the {@link List} of {@link LocationTrain}s for the train line - * @throws NullPointerException if {@code line} or {@code trains} is {@code null}, or if {@code trains} contains - * {@code null} elements - */ + /// Constructs a `TrainLocations`. + /// + /// @param line the train line associated with the locations + /// @param trains the [List] of [LocationTrain]s for the train line + /// @throws NullPointerException if `line` or `trains` is `null`, or if `trains` contains `null` elements public TrainLocations { Objects.requireNonNull(line); Objects.requireNonNull(trains); diff --git a/src/main/java/com/cta4j/train/station/StationsApi.java b/src/main/java/com/cta4j/train/station/StationsApi.java index 03f4f981..82248160 100644 --- a/src/main/java/com/cta4j/train/station/StationsApi.java +++ b/src/main/java/com/cta4j/train/station/StationsApi.java @@ -6,22 +6,18 @@ import java.util.List; -/** - * Provides access to station-related endpoints. - *

- * This API allows retrieval of station information, including station names, IDs, and other details. - *

- * NOTE: The CTA Train Tracker API does not provide an endpoint for retrieving station information. This API - * uses the City of Chicago's Data Portal as its data source by default. The URL used to retrieve station information - * is configurable to accommodate changes to the data source. - */ +/// Provides access to station-related endpoints. +/// +/// This API allows retrieval of station information, including station names, IDs, and other details. +/// +/// @apiNote The CTA Train Tracker API does not provide an endpoint for retrieving station information. This API uses +/// the City of Chicago's Data Portal as its data source by default. The URL used to retrieve station information is +/// configurable to accommodate changes to the data source. @NullMarked public interface StationsApi { - /** - * Retrieves all available stations. - * - * @return a {@link List} of all available {@link Station}s, or an empty {@link List} if no stations are found - * @throws Cta4jTrainException if the API returns an error response or the response cannot be parsed - */ + /// Retrieves all available stations. + /// + /// @return a [List] of all available [Station]s, or an empty [List] if no stations are found + /// @throws Cta4jTrainException if the API returns an error response or the response cannot be parsed List list(); } diff --git a/src/main/java/com/cta4j/train/station/model/CardinalDirection.java b/src/main/java/com/cta4j/train/station/model/CardinalDirection.java index c2c7c849..367c007f 100644 --- a/src/main/java/com/cta4j/train/station/model/CardinalDirection.java +++ b/src/main/java/com/cta4j/train/station/model/CardinalDirection.java @@ -4,38 +4,26 @@ import java.util.Objects; -/** - * Represents the four cardinal directions. - */ +/// Represents the four cardinal directions. @NullMarked public enum CardinalDirection { - /** - * Indicates the north direction. - */ + /// Indicates the north direction. NORTH, - /** - * Indicates the east direction. - */ + /// Indicates the east direction. EAST, - /** - * Indicates the south direction. - */ + /// Indicates the south direction. SOUTH, - /** - * Indicates the west direction. - */ + /// Indicates the west direction. WEST; - /** - * Returns the {@code CardinalDirection} corresponding to the given code. - * - * @param code the code representing the cardinal direction (e.g., "N", "E", "S", "W" or their full names) - * @return the corresponding {@code CardinalDirection} - * @throws IllegalArgumentException if the code does not correspond to any known cardinal direction - */ + /// Returns the `CardinalDirection` corresponding to the given code. + /// + /// @param code the code representing the cardinal direction (e.g., "N", "E", "S", "W" or their full names) + /// @return the corresponding `CardinalDirection` + /// @throws IllegalArgumentException if the code does not correspond to any known cardinal direction public static CardinalDirection fromCode(String code) { Objects.requireNonNull(code); diff --git a/src/main/java/com/cta4j/train/station/model/HumanAddress.java b/src/main/java/com/cta4j/train/station/model/HumanAddress.java index 4d1531be..c0d19579 100644 --- a/src/main/java/com/cta4j/train/station/model/HumanAddress.java +++ b/src/main/java/com/cta4j/train/station/model/HumanAddress.java @@ -4,14 +4,12 @@ import java.util.Objects; -/** - * Represents a human-readable address. - * - * @param address the street address - * @param city the city - * @param state the state - * @param zip the ZIP code - */ +/// Represents a human-readable address. +/// +/// @param address the street address +/// @param city the city +/// @param state the state +/// @param zip the ZIP code @NullMarked public record HumanAddress( String address, @@ -19,15 +17,13 @@ public record HumanAddress( String state, String zip ) { - /** - * Constructs a {@code HumanAddress}. - * - * @param address the street address - * @param city the city - * @param state the state - * @param zip the ZIP code - * @throws NullPointerException if {@code address}, {@code city}, {@code state}, or {@code zip} is {@code null} - */ + /// Constructs a `HumanAddress`. + /// + /// @param address the street address + /// @param city the city + /// @param state the state + /// @param zip the ZIP code + /// @throws NullPointerException if `address`, `city`, `state`, or `zip` is `null` public HumanAddress { Objects.requireNonNull(address); Objects.requireNonNull(city); diff --git a/src/main/java/com/cta4j/train/station/model/Location.java b/src/main/java/com/cta4j/train/station/model/Location.java index dc03a1fd..18beb8c4 100644 --- a/src/main/java/com/cta4j/train/station/model/Location.java +++ b/src/main/java/com/cta4j/train/station/model/Location.java @@ -6,27 +6,23 @@ import java.math.BigDecimal; import java.util.Objects; -/** - * Represents a geographical location with latitude and longitude, optionally including a human-readable address. - * - * @param latitude the latitude - * @param longitude the longitude - * @param humanAddress the human-readable address, or {@code null} if not available - */ +/// Represents a geographical location with latitude and longitude, optionally including a human-readable address. +/// +/// @param latitude the latitude +/// @param longitude the longitude +/// @param humanAddress the human-readable address, or `null` if not available @NullMarked public record Location( BigDecimal latitude, BigDecimal longitude, @Nullable HumanAddress humanAddress ) { - /** - * Constructs a {@code Location}. - * - * @param latitude the latitude - * @param longitude the longitude - * @param humanAddress the human-readable address, or {@code null} if not available - * @throws NullPointerException if {@code latitude} or {@code longitude} is {@code null} - */ + /// Constructs a `Location`. + /// + /// @param latitude the latitude + /// @param longitude the longitude + /// @param humanAddress the human-readable address, or `null` if not available + /// @throws NullPointerException if `latitude` or `longitude` is `null` public Location { Objects.requireNonNull(latitude); Objects.requireNonNull(longitude); diff --git a/src/main/java/com/cta4j/train/station/model/Station.java b/src/main/java/com/cta4j/train/station/model/Station.java index a8396cc4..301314a0 100644 --- a/src/main/java/com/cta4j/train/station/model/Station.java +++ b/src/main/java/com/cta4j/train/station/model/Station.java @@ -6,19 +6,17 @@ import java.util.Objects; import java.util.Set; -/** - * Represents a train station. - * - * @param stopId the unique stop identifier of this station - * @param direction the {@link CardinalDirection} of this station - * @param stopName the stop name of this station - * @param name the name of this station - * @param descriptiveName the descriptive name of this station - * @param mapId the map identifier of this station - * @param adaAccessible whether this station is ADA accessible - * @param lines the {@link Set} of {@link TrainLine}s that serve this station - * @param location the {@link Location} of this station - */ +/// Represents a train station. +/// +/// @param stopId the unique stop identifier of this station +/// @param direction the [CardinalDirection] of this station +/// @param stopName the stop name of this station +/// @param name the name of this station +/// @param descriptiveName the descriptive name of this station +/// @param mapId the map identifier of this station +/// @param adaAccessible whether this station is ADA accessible +/// @param lines the [Set] of [TrainLine]s that serve this station +/// @param location the [Location] of this station @NullMarked public record Station( String stopId, @@ -31,22 +29,19 @@ public record Station( Set lines, Location location ) { - /** - * Constructs a {@code Station}. - * - * @param stopId the unique stop identifier of the station - * @param direction the {@link CardinalDirection} of the station - * @param stopName the stop name of the station - * @param name the name of the station - * @param descriptiveName the descriptive name of the station - * @param mapId the map identifier of the station - * @param adaAccessible whether the station is ADA accessible - * @param lines the {@link Set} of {@link TrainLine}s that serve the station - * @param location the {@link Location} of the station - * @throws NullPointerException if {@code stopId}, {@code direction}, {@code stopName}, {@code name}, - * {@code descriptiveName}, {@code mapId}, {@code lines}, or {@code location} is {@code null}, or if any element of - * {@code lines} is {@code null} - */ + /// Constructs a `Station`. + /// + /// @param stopId the unique stop identifier of the station + /// @param direction the [CardinalDirection] of the station + /// @param stopName the stop name of the station + /// @param name the name of the station + /// @param descriptiveName the descriptive name of the station + /// @param mapId the map identifier of the station + /// @param adaAccessible whether the station is ADA accessible + /// @param lines the [Set] of [TrainLine]s that serve the station + /// @param location the [Location] of the station + /// @throws NullPointerException if `stopId`, `direction`, `stopName`, `name`, `descriptiveName`, `mapId`, `lines`, + /// or `location` is `null`, or if any element of `lines` is `null` public Station { Objects.requireNonNull(stopId); Objects.requireNonNull(direction); From a855271245f1690b536471774db76e18d6aab387 Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Wed, 29 Jul 2026 18:49:59 -0500 Subject: [PATCH 41/60] Javadoc updates and package-info.java files --- CLAUDE.md | 9 ++++++--- .../alert/common/exception/package-info.java | 2 ++ .../cta4j/alert/common/model/package-info.java | 3 +++ .../com/cta4j/alert/common/package-info.java | 3 +++ .../exception/DetailedAlertsErrorCode.java | 18 +++++++++--------- .../detailedalert/exception/package-info.java | 2 ++ .../detailedalert/model/package-info.java | 2 ++ .../alert/detailedalert/package-info.java | 2 ++ .../detailedalert/query/package-info.java | 3 +++ .../java/com/cta4j/alert/package-info.java | 2 ++ .../exception/RouteStatusErrorCode.java | 8 ++++---- .../routestatus/exception/package-info.java | 2 ++ .../alert/routestatus/model/package-info.java | 2 ++ .../cta4j/alert/routestatus/package-info.java | 3 +++ .../bus/common/exception/package-info.java | 2 ++ .../com/cta4j/bus/common/package-info.java | 3 +++ .../cta4j/bus/detour/model/package-info.java | 3 +++ .../com/cta4j/bus/detour/package-info.java | 2 ++ .../com/cta4j/bus/direction/package-info.java | 2 ++ .../cta4j/bus/locale/model/package-info.java | 2 ++ .../com/cta4j/bus/locale/package-info.java | 2 ++ src/main/java/com/cta4j/bus/package-info.java | 3 +++ .../cta4j/bus/pattern/model/package-info.java | 3 +++ .../com/cta4j/bus/pattern/package-info.java | 2 ++ .../bus/prediction/model/package-info.java | 3 +++ .../com/cta4j/bus/prediction/package-info.java | 2 ++ .../bus/prediction/query/package-info.java | 3 +++ .../cta4j/bus/route/model/package-info.java | 2 ++ .../java/com/cta4j/bus/route/package-info.java | 2 ++ .../com/cta4j/bus/stop/model/package-info.java | 2 ++ .../java/com/cta4j/bus/stop/package-info.java | 2 ++ .../cta4j/bus/vehicle/model/package-info.java | 3 +++ .../com/cta4j/bus/vehicle/package-info.java | 2 ++ .../cta4j/common/exception/package-info.java | 2 ++ .../com/cta4j/common/geo/package-info.java | 2 ++ .../java/com/cta4j/common/package-info.java | 3 +++ .../arrival/exception/ArrivalsErrorCode.java | 2 +- .../train/arrival/exception/package-info.java | 2 ++ .../com/cta4j/train/arrival/package-info.java | 2 ++ .../train/arrival/query/package-info.java | 3 +++ .../train/common/exception/package-info.java | 2 ++ .../cta4j/train/common/model/package-info.java | 3 +++ .../com/cta4j/train/common/package-info.java | 2 ++ .../follow/exception/FollowErrorCode.java | 2 +- .../train/follow/exception/package-info.java | 2 ++ .../cta4j/train/follow/model/package-info.java | 3 +++ .../com/cta4j/train/follow/package-info.java | 2 ++ .../location/exception/LocationsErrorCode.java | 2 +- .../train/location/exception/package-info.java | 2 ++ .../train/location/model/package-info.java | 3 +++ .../com/cta4j/train/location/package-info.java | 2 ++ .../java/com/cta4j/train/package-info.java | 3 +++ .../train/station/model/package-info.java | 3 +++ .../com/cta4j/train/station/package-info.java | 2 ++ 54 files changed, 136 insertions(+), 19 deletions(-) create mode 100644 src/main/java/com/cta4j/alert/common/exception/package-info.java create mode 100644 src/main/java/com/cta4j/alert/common/model/package-info.java create mode 100644 src/main/java/com/cta4j/alert/common/package-info.java create mode 100644 src/main/java/com/cta4j/alert/detailedalert/exception/package-info.java create mode 100644 src/main/java/com/cta4j/alert/detailedalert/model/package-info.java create mode 100644 src/main/java/com/cta4j/alert/detailedalert/package-info.java create mode 100644 src/main/java/com/cta4j/alert/detailedalert/query/package-info.java create mode 100644 src/main/java/com/cta4j/alert/package-info.java create mode 100644 src/main/java/com/cta4j/alert/routestatus/exception/package-info.java create mode 100644 src/main/java/com/cta4j/alert/routestatus/model/package-info.java create mode 100644 src/main/java/com/cta4j/alert/routestatus/package-info.java create mode 100644 src/main/java/com/cta4j/bus/common/exception/package-info.java create mode 100644 src/main/java/com/cta4j/bus/common/package-info.java create mode 100644 src/main/java/com/cta4j/bus/detour/model/package-info.java create mode 100644 src/main/java/com/cta4j/bus/detour/package-info.java create mode 100644 src/main/java/com/cta4j/bus/direction/package-info.java create mode 100644 src/main/java/com/cta4j/bus/locale/model/package-info.java create mode 100644 src/main/java/com/cta4j/bus/locale/package-info.java create mode 100644 src/main/java/com/cta4j/bus/package-info.java create mode 100644 src/main/java/com/cta4j/bus/pattern/model/package-info.java create mode 100644 src/main/java/com/cta4j/bus/pattern/package-info.java create mode 100644 src/main/java/com/cta4j/bus/prediction/model/package-info.java create mode 100644 src/main/java/com/cta4j/bus/prediction/package-info.java create mode 100644 src/main/java/com/cta4j/bus/prediction/query/package-info.java create mode 100644 src/main/java/com/cta4j/bus/route/model/package-info.java create mode 100644 src/main/java/com/cta4j/bus/route/package-info.java create mode 100644 src/main/java/com/cta4j/bus/stop/model/package-info.java create mode 100644 src/main/java/com/cta4j/bus/stop/package-info.java create mode 100644 src/main/java/com/cta4j/bus/vehicle/model/package-info.java create mode 100644 src/main/java/com/cta4j/bus/vehicle/package-info.java create mode 100644 src/main/java/com/cta4j/common/exception/package-info.java create mode 100644 src/main/java/com/cta4j/common/geo/package-info.java create mode 100644 src/main/java/com/cta4j/common/package-info.java create mode 100644 src/main/java/com/cta4j/train/arrival/exception/package-info.java create mode 100644 src/main/java/com/cta4j/train/arrival/package-info.java create mode 100644 src/main/java/com/cta4j/train/arrival/query/package-info.java create mode 100644 src/main/java/com/cta4j/train/common/exception/package-info.java create mode 100644 src/main/java/com/cta4j/train/common/model/package-info.java create mode 100644 src/main/java/com/cta4j/train/common/package-info.java create mode 100644 src/main/java/com/cta4j/train/follow/exception/package-info.java create mode 100644 src/main/java/com/cta4j/train/follow/model/package-info.java create mode 100644 src/main/java/com/cta4j/train/follow/package-info.java create mode 100644 src/main/java/com/cta4j/train/location/exception/package-info.java create mode 100644 src/main/java/com/cta4j/train/location/model/package-info.java create mode 100644 src/main/java/com/cta4j/train/location/package-info.java create mode 100644 src/main/java/com/cta4j/train/package-info.java create mode 100644 src/main/java/com/cta4j/train/station/model/package-info.java create mode 100644 src/main/java/com/cta4j/train/station/package-info.java diff --git a/CLAUDE.md b/CLAUDE.md index 78dd9ade..887f650d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -126,6 +126,9 @@ References: (`model/`), and builders always. Wire records (`internal/wire/`), mappers, and `*ApiImpl` classes are `@ApiStatus.Internal` and are not documented unless the "why" is non-obvious (per Code Style). -- **Package docs:** Each top-level feature package (e.g. `bus.route`, - `train.arrivals`) gets a `package-info.java` with a one-paragraph summary - of the feature's responsibility, written in the same Markdown style. +- **Package docs:** Every package containing at least one non-internal + type (public `*Api` interfaces, public domain models, builders — per + "What gets documented" above) gets a `package-info.java` with a + one-paragraph summary of the package's responsibility, written in the + same Markdown style. Packages containing only `internal/wire`, mapper, + or `*ApiImpl` classes do not require a package-info.java. diff --git a/src/main/java/com/cta4j/alert/common/exception/package-info.java b/src/main/java/com/cta4j/alert/common/exception/package-info.java new file mode 100644 index 00000000..cd9f763d --- /dev/null +++ b/src/main/java/com/cta4j/alert/common/exception/package-info.java @@ -0,0 +1,2 @@ +/// Base exception type shared across the CTA Alerts API's detailed alert and route status sub-APIs. +package com.cta4j.alert.common.exception; \ No newline at end of file diff --git a/src/main/java/com/cta4j/alert/common/model/package-info.java b/src/main/java/com/cta4j/alert/common/model/package-info.java new file mode 100644 index 00000000..1c8a9133 --- /dev/null +++ b/src/main/java/com/cta4j/alert/common/model/package-info.java @@ -0,0 +1,3 @@ +/// Domain model types shared across the CTA Alerts API's detailed alert and route status sub-APIs, such as train +/// line and service type designators. +package com.cta4j.alert.common.model; \ No newline at end of file diff --git a/src/main/java/com/cta4j/alert/common/package-info.java b/src/main/java/com/cta4j/alert/common/package-info.java new file mode 100644 index 00000000..2881a266 --- /dev/null +++ b/src/main/java/com/cta4j/alert/common/package-info.java @@ -0,0 +1,3 @@ +/// Shared types, configuration, and internal plumbing used across the CTA Alerts API's detailed alert and route +/// status sub-APIs. +package com.cta4j.alert.common; \ No newline at end of file diff --git a/src/main/java/com/cta4j/alert/detailedalert/exception/DetailedAlertsErrorCode.java b/src/main/java/com/cta4j/alert/detailedalert/exception/DetailedAlertsErrorCode.java index 355fdf7a..c44f6499 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/exception/DetailedAlertsErrorCode.java +++ b/src/main/java/com/cta4j/alert/detailedalert/exception/DetailedAlertsErrorCode.java @@ -14,33 +14,33 @@ public enum DetailedAlertsErrorCode { /// Indicates that there are no active alerts based on the provided filter criteria. NO_ACTIVE_ALERTS_FOR_FILTER(50), - /// Indicates that the provided "activeonly" value is invalid. + /// Indicates that the provided `activeonly` value is invalid. INVALID_ACTIVEONLY(100), - /// Indicates that the provided "accessibility" value is invalid. + /// Indicates that the provided `accessibility` value is invalid. INVALID_ACCESSIBILITY(101), - /// Indicates that the provided "planned" value is invalid. + /// Indicates that the provided `planned` value is invalid. INVALID_PLANNED(102), /// Indicates that the provided station ID is not an integer. STATIONID_NOT_INTEGER(103), - /// Indicates that the provided "bystartdate" value is not a valid date in "yyyyMMdd" format. + /// Indicates that the provided `bystartdate` value is not a valid date in `yyyyMMdd` format. INVALID_BYSTARTDATE(104), - /// Indicates that the provided "recentdays" value is not an integer. + /// Indicates that the provided `recentdays` value is not an integer. RECENTDAYS_NOT_INTEGER(105), - /// Indicates that the "routeid" and "stationid" parameters were both provided, which is not allowed. + /// Indicates that the `routeid` and `stationid` parameters were both provided, which is not allowed. ROUTEID_STATIONID_CONFLICT(106), - /// Indicates that the "recentdays" and "bystartdate" parameters were both provided, which is not allowed. + /// Indicates that the `recentdays` and `bystartdate` parameters were both provided, which is not allowed. RECENTDAYS_BYSTARTDATE_CONFLICT(107), /// Indicates that the query string contains a parameter that is not recognized by the API. The supported API - /// parameters are "activeonly", "accessibility", "planned", "routeid", "stationid", "bystartdate", "recentdays", - /// and "outputType". + /// parameters are `activeonly`, `accessibility`, `planned`, `routeid`, `stationid`, `bystartdate`, `recentdays`, + /// and `outputType`. INVALID_PARAMETER(500), /// Indicates that the server encountered an unexpected error that prevented it from fulfilling the request. diff --git a/src/main/java/com/cta4j/alert/detailedalert/exception/package-info.java b/src/main/java/com/cta4j/alert/detailedalert/exception/package-info.java new file mode 100644 index 00000000..92c2b444 --- /dev/null +++ b/src/main/java/com/cta4j/alert/detailedalert/exception/package-info.java @@ -0,0 +1,2 @@ +/// Exception type and error code enum thrown by the CTA Detailed Alerts API. +package com.cta4j.alert.detailedalert.exception; \ No newline at end of file diff --git a/src/main/java/com/cta4j/alert/detailedalert/model/package-info.java b/src/main/java/com/cta4j/alert/detailedalert/model/package-info.java new file mode 100644 index 00000000..2df035c8 --- /dev/null +++ b/src/main/java/com/cta4j/alert/detailedalert/model/package-info.java @@ -0,0 +1,2 @@ +/// Domain model types returned by the CTA Detailed Alerts API, representing alerts and their impacted services. +package com.cta4j.alert.detailedalert.model; \ No newline at end of file diff --git a/src/main/java/com/cta4j/alert/detailedalert/package-info.java b/src/main/java/com/cta4j/alert/detailedalert/package-info.java new file mode 100644 index 00000000..44af4cb9 --- /dev/null +++ b/src/main/java/com/cta4j/alert/detailedalert/package-info.java @@ -0,0 +1,2 @@ +/// Retrieval of detailed CTA service alerts, filterable by bus route ID, train line, or station ID. +package com.cta4j.alert.detailedalert; \ No newline at end of file diff --git a/src/main/java/com/cta4j/alert/detailedalert/query/package-info.java b/src/main/java/com/cta4j/alert/detailedalert/query/package-info.java new file mode 100644 index 00000000..22c6f3ce --- /dev/null +++ b/src/main/java/com/cta4j/alert/detailedalert/query/package-info.java @@ -0,0 +1,3 @@ +/// Query builder types for filtering requests to the CTA Detailed Alerts API by bus route ID, train line, or +/// station ID. +package com.cta4j.alert.detailedalert.query; \ No newline at end of file diff --git a/src/main/java/com/cta4j/alert/package-info.java b/src/main/java/com/cta4j/alert/package-info.java new file mode 100644 index 00000000..9c6d6727 --- /dev/null +++ b/src/main/java/com/cta4j/alert/package-info.java @@ -0,0 +1,2 @@ +/// Entry point for the CTA Alerts API, exposing sub-APIs for detailed alerts and route status information. +package com.cta4j.alert; \ No newline at end of file diff --git a/src/main/java/com/cta4j/alert/routestatus/exception/RouteStatusErrorCode.java b/src/main/java/com/cta4j/alert/routestatus/exception/RouteStatusErrorCode.java index 103ada94..4afc22a1 100644 --- a/src/main/java/com/cta4j/alert/routestatus/exception/RouteStatusErrorCode.java +++ b/src/main/java/com/cta4j/alert/routestatus/exception/RouteStatusErrorCode.java @@ -20,17 +20,17 @@ public enum RouteStatusErrorCode { /// Indicates that the provided service type is invalid. INVALID_TYPE(101), - /// Indicates that the "routeid" and "stationid" parameters were both provided, which is not allowed. + /// Indicates that the `routeid` and `stationid` parameters were both provided, which is not allowed. ROUTEID_STATIONID_CONFLICT(102), - /// Indicates that the "routeid" and "type" parameters were both provided, which is not allowed. + /// Indicates that the `routeid` and `type` parameters were both provided, which is not allowed. ROUTEID_TYPE_CONFLICT(103), - /// Indicates that the "stationid" and "type" parameters were both provided, which is not allowed. + /// Indicates that the `stationid` and `type` parameters were both provided, which is not allowed. STATIONID_TYPE_CONFLICT(104), /// Indicates that the query string contains a parameter that is not recognized by the API. The supported API - /// parameters are "type", "routeid", "stationid", and "outputType". + /// parameters are `type`, `routeid`, `stationid`, and `outputType`. INVALID_PARAMETER(500), /// Indicates that the server encountered an unexpected error that prevented it from fulfilling the request. diff --git a/src/main/java/com/cta4j/alert/routestatus/exception/package-info.java b/src/main/java/com/cta4j/alert/routestatus/exception/package-info.java new file mode 100644 index 00000000..88ff4667 --- /dev/null +++ b/src/main/java/com/cta4j/alert/routestatus/exception/package-info.java @@ -0,0 +1,2 @@ +/// Exception type and error code enum thrown by the CTA Route Status API. +package com.cta4j.alert.routestatus.exception; \ No newline at end of file diff --git a/src/main/java/com/cta4j/alert/routestatus/model/package-info.java b/src/main/java/com/cta4j/alert/routestatus/model/package-info.java new file mode 100644 index 00000000..65345459 --- /dev/null +++ b/src/main/java/com/cta4j/alert/routestatus/model/package-info.java @@ -0,0 +1,2 @@ +/// Domain model type returned by the CTA Route Status API, representing the status of a single route. +package com.cta4j.alert.routestatus.model; \ No newline at end of file diff --git a/src/main/java/com/cta4j/alert/routestatus/package-info.java b/src/main/java/com/cta4j/alert/routestatus/package-info.java new file mode 100644 index 00000000..ad99ad2e --- /dev/null +++ b/src/main/java/com/cta4j/alert/routestatus/package-info.java @@ -0,0 +1,3 @@ +/// Retrieval of the current status of CTA bus and train routes, filterable by service type, bus route ID, train +/// line, or station ID. +package com.cta4j.alert.routestatus; \ No newline at end of file diff --git a/src/main/java/com/cta4j/bus/common/exception/package-info.java b/src/main/java/com/cta4j/bus/common/exception/package-info.java new file mode 100644 index 00000000..b76a9e5a --- /dev/null +++ b/src/main/java/com/cta4j/bus/common/exception/package-info.java @@ -0,0 +1,2 @@ +/// Base exception type thrown by the CTA Bus Tracker API's sub-APIs. +package com.cta4j.bus.common.exception; \ No newline at end of file diff --git a/src/main/java/com/cta4j/bus/common/package-info.java b/src/main/java/com/cta4j/bus/common/package-info.java new file mode 100644 index 00000000..c97d4ae0 --- /dev/null +++ b/src/main/java/com/cta4j/bus/common/package-info.java @@ -0,0 +1,3 @@ +/// Shared types, configuration, and internal plumbing used across the CTA Bus Tracker API's sub-APIs, including the +/// error-handling utilities common to bustime-response endpoints. +package com.cta4j.bus.common; \ No newline at end of file diff --git a/src/main/java/com/cta4j/bus/detour/model/package-info.java b/src/main/java/com/cta4j/bus/detour/model/package-info.java new file mode 100644 index 00000000..14be41a5 --- /dev/null +++ b/src/main/java/com/cta4j/bus/detour/model/package-info.java @@ -0,0 +1,3 @@ +/// Domain model types returned by the CTA Bus Tracker API's detours endpoint, representing an active detour and +/// the route directions it affects. +package com.cta4j.bus.detour.model; \ No newline at end of file diff --git a/src/main/java/com/cta4j/bus/detour/package-info.java b/src/main/java/com/cta4j/bus/detour/package-info.java new file mode 100644 index 00000000..614df10b --- /dev/null +++ b/src/main/java/com/cta4j/bus/detour/package-info.java @@ -0,0 +1,2 @@ +/// Retrieval of active CTA bus detours and the route directions they affect. +package com.cta4j.bus.detour; \ No newline at end of file diff --git a/src/main/java/com/cta4j/bus/direction/package-info.java b/src/main/java/com/cta4j/bus/direction/package-info.java new file mode 100644 index 00000000..f9ba1e6b --- /dev/null +++ b/src/main/java/com/cta4j/bus/direction/package-info.java @@ -0,0 +1,2 @@ +/// Retrieval of the available travel directions for a given CTA bus route. +package com.cta4j.bus.direction; \ No newline at end of file diff --git a/src/main/java/com/cta4j/bus/locale/model/package-info.java b/src/main/java/com/cta4j/bus/locale/model/package-info.java new file mode 100644 index 00000000..7e1a37e5 --- /dev/null +++ b/src/main/java/com/cta4j/bus/locale/model/package-info.java @@ -0,0 +1,2 @@ +/// Domain model type returned by the CTA Bus Tracker API's locales endpoint, representing a supported locale. +package com.cta4j.bus.locale.model; \ No newline at end of file diff --git a/src/main/java/com/cta4j/bus/locale/package-info.java b/src/main/java/com/cta4j/bus/locale/package-info.java new file mode 100644 index 00000000..bad13c3a --- /dev/null +++ b/src/main/java/com/cta4j/bus/locale/package-info.java @@ -0,0 +1,2 @@ +/// Retrieval of the locales supported by the CTA Bus Tracker API. +package com.cta4j.bus.locale; \ No newline at end of file diff --git a/src/main/java/com/cta4j/bus/package-info.java b/src/main/java/com/cta4j/bus/package-info.java new file mode 100644 index 00000000..94286cb9 --- /dev/null +++ b/src/main/java/com/cta4j/bus/package-info.java @@ -0,0 +1,3 @@ +/// Entry point for the CTA Bus Tracker API, exposing sub-APIs for vehicles, routes, directions, stops, patterns, +/// predictions, locales, detours, and system time. +package com.cta4j.bus; \ No newline at end of file diff --git a/src/main/java/com/cta4j/bus/pattern/model/package-info.java b/src/main/java/com/cta4j/bus/pattern/model/package-info.java new file mode 100644 index 00000000..bd21f33c --- /dev/null +++ b/src/main/java/com/cta4j/bus/pattern/model/package-info.java @@ -0,0 +1,3 @@ +/// Domain model types returned by the CTA Bus Tracker API's patterns endpoint, representing a route pattern and +/// its constituent points. +package com.cta4j.bus.pattern.model; \ No newline at end of file diff --git a/src/main/java/com/cta4j/bus/pattern/package-info.java b/src/main/java/com/cta4j/bus/pattern/package-info.java new file mode 100644 index 00000000..d969e18d --- /dev/null +++ b/src/main/java/com/cta4j/bus/pattern/package-info.java @@ -0,0 +1,2 @@ +/// Retrieval of CTA bus route patterns, describing the sequence of points a route follows. +package com.cta4j.bus.pattern; \ No newline at end of file diff --git a/src/main/java/com/cta4j/bus/prediction/model/package-info.java b/src/main/java/com/cta4j/bus/prediction/model/package-info.java new file mode 100644 index 00000000..f2bf4010 --- /dev/null +++ b/src/main/java/com/cta4j/bus/prediction/model/package-info.java @@ -0,0 +1,3 @@ +/// Domain model types returned by the CTA Bus Tracker API's predictions endpoint, representing an arrival +/// prediction and its metadata. +package com.cta4j.bus.prediction.model; \ No newline at end of file diff --git a/src/main/java/com/cta4j/bus/prediction/package-info.java b/src/main/java/com/cta4j/bus/prediction/package-info.java new file mode 100644 index 00000000..db08688e --- /dev/null +++ b/src/main/java/com/cta4j/bus/prediction/package-info.java @@ -0,0 +1,2 @@ +/// Retrieval of real-time CTA bus arrival predictions for stops and vehicles. +package com.cta4j.bus.prediction; \ No newline at end of file diff --git a/src/main/java/com/cta4j/bus/prediction/query/package-info.java b/src/main/java/com/cta4j/bus/prediction/query/package-info.java new file mode 100644 index 00000000..af6be0b9 --- /dev/null +++ b/src/main/java/com/cta4j/bus/prediction/query/package-info.java @@ -0,0 +1,3 @@ +/// Query builder types for filtering requests to the CTA Bus Tracker API's predictions endpoint by stop or +/// vehicle. +package com.cta4j.bus.prediction.query; \ No newline at end of file diff --git a/src/main/java/com/cta4j/bus/route/model/package-info.java b/src/main/java/com/cta4j/bus/route/model/package-info.java new file mode 100644 index 00000000..1162125c --- /dev/null +++ b/src/main/java/com/cta4j/bus/route/model/package-info.java @@ -0,0 +1,2 @@ +/// Domain model type returned by the CTA Bus Tracker API's routes endpoint, representing a single bus route. +package com.cta4j.bus.route.model; \ No newline at end of file diff --git a/src/main/java/com/cta4j/bus/route/package-info.java b/src/main/java/com/cta4j/bus/route/package-info.java new file mode 100644 index 00000000..fc15a6c4 --- /dev/null +++ b/src/main/java/com/cta4j/bus/route/package-info.java @@ -0,0 +1,2 @@ +/// Retrieval of all available CTA bus routes. +package com.cta4j.bus.route; \ No newline at end of file diff --git a/src/main/java/com/cta4j/bus/stop/model/package-info.java b/src/main/java/com/cta4j/bus/stop/model/package-info.java new file mode 100644 index 00000000..952a06db --- /dev/null +++ b/src/main/java/com/cta4j/bus/stop/model/package-info.java @@ -0,0 +1,2 @@ +/// Domain model type returned by the CTA Bus Tracker API's stops endpoint, representing a single bus stop. +package com.cta4j.bus.stop.model; \ No newline at end of file diff --git a/src/main/java/com/cta4j/bus/stop/package-info.java b/src/main/java/com/cta4j/bus/stop/package-info.java new file mode 100644 index 00000000..5bb265b4 --- /dev/null +++ b/src/main/java/com/cta4j/bus/stop/package-info.java @@ -0,0 +1,2 @@ +/// Retrieval of CTA bus stops for a given route and direction. +package com.cta4j.bus.stop; \ No newline at end of file diff --git a/src/main/java/com/cta4j/bus/vehicle/model/package-info.java b/src/main/java/com/cta4j/bus/vehicle/model/package-info.java new file mode 100644 index 00000000..77ba2559 --- /dev/null +++ b/src/main/java/com/cta4j/bus/vehicle/model/package-info.java @@ -0,0 +1,3 @@ +/// Domain model types returned by the CTA Bus Tracker API's vehicles endpoint, representing a vehicle and its +/// metadata. +package com.cta4j.bus.vehicle.model; \ No newline at end of file diff --git a/src/main/java/com/cta4j/bus/vehicle/package-info.java b/src/main/java/com/cta4j/bus/vehicle/package-info.java new file mode 100644 index 00000000..c505a053 --- /dev/null +++ b/src/main/java/com/cta4j/bus/vehicle/package-info.java @@ -0,0 +1,2 @@ +/// Retrieval of real-time CTA bus vehicle locations and metadata. +package com.cta4j.bus.vehicle; \ No newline at end of file diff --git a/src/main/java/com/cta4j/common/exception/package-info.java b/src/main/java/com/cta4j/common/exception/package-info.java new file mode 100644 index 00000000..0da83f07 --- /dev/null +++ b/src/main/java/com/cta4j/common/exception/package-info.java @@ -0,0 +1,2 @@ +/// Base exception type for the SDK, extended by every transit-specific exception type. +package com.cta4j.common.exception; \ No newline at end of file diff --git a/src/main/java/com/cta4j/common/geo/package-info.java b/src/main/java/com/cta4j/common/geo/package-info.java new file mode 100644 index 00000000..ec082a94 --- /dev/null +++ b/src/main/java/com/cta4j/common/geo/package-info.java @@ -0,0 +1,2 @@ +/// Domain model type representing geographic coordinates, shared across the bus and train APIs. +package com.cta4j.common.geo; \ No newline at end of file diff --git a/src/main/java/com/cta4j/common/package-info.java b/src/main/java/com/cta4j/common/package-info.java new file mode 100644 index 00000000..0b3bf438 --- /dev/null +++ b/src/main/java/com/cta4j/common/package-info.java @@ -0,0 +1,3 @@ +/// Cross-cutting types shared across the bus and train APIs, such as geographic coordinates and the base SDK +/// exception type. +package com.cta4j.common; \ No newline at end of file diff --git a/src/main/java/com/cta4j/train/arrival/exception/ArrivalsErrorCode.java b/src/main/java/com/cta4j/train/arrival/exception/ArrivalsErrorCode.java index 409c2150..a7f345ab 100644 --- a/src/main/java/com/cta4j/train/arrival/exception/ArrivalsErrorCode.java +++ b/src/main/java/com/cta4j/train/arrival/exception/ArrivalsErrorCode.java @@ -48,7 +48,7 @@ public enum ArrivalsErrorCode { STPID_NOT_INTEGER(112), /// Indicates that the query string contains a parameter that is not recognized by the API. The supported API - /// parameters are "mapid", "key", "rt", "stpid", and "max". + /// parameters are `mapid`, `key`, `rt`, `stpid`, and `max`. INVALID_PARAMETER(500), /// Indicates that the server encountered an unexpected error that prevented it from fulfilling the request. diff --git a/src/main/java/com/cta4j/train/arrival/exception/package-info.java b/src/main/java/com/cta4j/train/arrival/exception/package-info.java new file mode 100644 index 00000000..6a08ac2f --- /dev/null +++ b/src/main/java/com/cta4j/train/arrival/exception/package-info.java @@ -0,0 +1,2 @@ +/// Exception type and error code enum thrown by the CTA Train Tracker API's arrivals endpoint. +package com.cta4j.train.arrival.exception; \ No newline at end of file diff --git a/src/main/java/com/cta4j/train/arrival/package-info.java b/src/main/java/com/cta4j/train/arrival/package-info.java new file mode 100644 index 00000000..2d5441c1 --- /dev/null +++ b/src/main/java/com/cta4j/train/arrival/package-info.java @@ -0,0 +1,2 @@ +/// Retrieval of real-time CTA train arrival predictions for stations and stops. +package com.cta4j.train.arrival; \ No newline at end of file diff --git a/src/main/java/com/cta4j/train/arrival/query/package-info.java b/src/main/java/com/cta4j/train/arrival/query/package-info.java new file mode 100644 index 00000000..d6208882 --- /dev/null +++ b/src/main/java/com/cta4j/train/arrival/query/package-info.java @@ -0,0 +1,3 @@ +/// Query builder types for filtering requests to the CTA Train Tracker API's arrivals endpoint by station or +/// stop. +package com.cta4j.train.arrival.query; \ No newline at end of file diff --git a/src/main/java/com/cta4j/train/common/exception/package-info.java b/src/main/java/com/cta4j/train/common/exception/package-info.java new file mode 100644 index 00000000..fd9e3bea --- /dev/null +++ b/src/main/java/com/cta4j/train/common/exception/package-info.java @@ -0,0 +1,2 @@ +/// Base exception type thrown by the CTA Train Tracker API's sub-APIs. +package com.cta4j.train.common.exception; \ No newline at end of file diff --git a/src/main/java/com/cta4j/train/common/model/package-info.java b/src/main/java/com/cta4j/train/common/model/package-info.java new file mode 100644 index 00000000..fda8c5a7 --- /dev/null +++ b/src/main/java/com/cta4j/train/common/model/package-info.java @@ -0,0 +1,3 @@ +/// Domain model types shared across the CTA Train Tracker API's sub-APIs, such as arrivals and train line and +/// direction designators. +package com.cta4j.train.common.model; \ No newline at end of file diff --git a/src/main/java/com/cta4j/train/common/package-info.java b/src/main/java/com/cta4j/train/common/package-info.java new file mode 100644 index 00000000..cf11408c --- /dev/null +++ b/src/main/java/com/cta4j/train/common/package-info.java @@ -0,0 +1,2 @@ +/// Shared types, configuration, and internal plumbing used across the CTA Train Tracker API's sub-APIs. +package com.cta4j.train.common; \ No newline at end of file diff --git a/src/main/java/com/cta4j/train/follow/exception/FollowErrorCode.java b/src/main/java/com/cta4j/train/follow/exception/FollowErrorCode.java index 8201d946..d509fa56 100644 --- a/src/main/java/com/cta4j/train/follow/exception/FollowErrorCode.java +++ b/src/main/java/com/cta4j/train/follow/exception/FollowErrorCode.java @@ -18,7 +18,7 @@ public enum FollowErrorCode { DAILY_LIMIT_EXCEEDED(102), /// Indicates that the query string contains a parameter that is not recognized by the API. The supported API - /// parameters are "runnumber" and "key". + /// parameters are `runnumber` and `key`. INVALID_PARAMETER(500), /// Indicates that the specified run number does not correspond to any known train run. diff --git a/src/main/java/com/cta4j/train/follow/exception/package-info.java b/src/main/java/com/cta4j/train/follow/exception/package-info.java new file mode 100644 index 00000000..4babce45 --- /dev/null +++ b/src/main/java/com/cta4j/train/follow/exception/package-info.java @@ -0,0 +1,2 @@ +/// Exception type and error code enum thrown by the CTA Train Tracker API's follow endpoint. +package com.cta4j.train.follow.exception; \ No newline at end of file diff --git a/src/main/java/com/cta4j/train/follow/model/package-info.java b/src/main/java/com/cta4j/train/follow/model/package-info.java new file mode 100644 index 00000000..7e8cbb08 --- /dev/null +++ b/src/main/java/com/cta4j/train/follow/model/package-info.java @@ -0,0 +1,3 @@ +/// Domain model type returned by the CTA Train Tracker API's follow endpoint, representing a train's upcoming +/// predictions. +package com.cta4j.train.follow.model; \ No newline at end of file diff --git a/src/main/java/com/cta4j/train/follow/package-info.java b/src/main/java/com/cta4j/train/follow/package-info.java new file mode 100644 index 00000000..25a1c5a2 --- /dev/null +++ b/src/main/java/com/cta4j/train/follow/package-info.java @@ -0,0 +1,2 @@ +/// Retrieval of the upcoming predictions for a single CTA train run as it continues along its route. +package com.cta4j.train.follow; \ No newline at end of file diff --git a/src/main/java/com/cta4j/train/location/exception/LocationsErrorCode.java b/src/main/java/com/cta4j/train/location/exception/LocationsErrorCode.java index 2ad25cd2..37bc493d 100644 --- a/src/main/java/com/cta4j/train/location/exception/LocationsErrorCode.java +++ b/src/main/java/com/cta4j/train/location/exception/LocationsErrorCode.java @@ -25,7 +25,7 @@ public enum LocationsErrorCode { TOO_MANY_ROUTES(107), /// Indicates that the query string contains a parameter that is not recognized by the API. The supported API - /// parameters are "rt" and "key". + /// parameters are `rt` and `key`. INVALID_PARAMETER(500), /// Indicates that an unknown error occurred that does not match any of the defined error codes. diff --git a/src/main/java/com/cta4j/train/location/exception/package-info.java b/src/main/java/com/cta4j/train/location/exception/package-info.java new file mode 100644 index 00000000..513f865d --- /dev/null +++ b/src/main/java/com/cta4j/train/location/exception/package-info.java @@ -0,0 +1,2 @@ +/// Exception type and error code enum thrown by the CTA Train Tracker API's locations endpoint. +package com.cta4j.train.location.exception; \ No newline at end of file diff --git a/src/main/java/com/cta4j/train/location/model/package-info.java b/src/main/java/com/cta4j/train/location/model/package-info.java new file mode 100644 index 00000000..8ea2f922 --- /dev/null +++ b/src/main/java/com/cta4j/train/location/model/package-info.java @@ -0,0 +1,3 @@ +/// Domain model types returned by the CTA Train Tracker API's locations endpoint, representing a train's +/// location and its containing collection. +package com.cta4j.train.location.model; \ No newline at end of file diff --git a/src/main/java/com/cta4j/train/location/package-info.java b/src/main/java/com/cta4j/train/location/package-info.java new file mode 100644 index 00000000..9fefd7ff --- /dev/null +++ b/src/main/java/com/cta4j/train/location/package-info.java @@ -0,0 +1,2 @@ +/// Retrieval of the real-time locations of CTA trains on a given route. +package com.cta4j.train.location; \ No newline at end of file diff --git a/src/main/java/com/cta4j/train/package-info.java b/src/main/java/com/cta4j/train/package-info.java new file mode 100644 index 00000000..5570e9ca --- /dev/null +++ b/src/main/java/com/cta4j/train/package-info.java @@ -0,0 +1,3 @@ +/// Entry point for the CTA Train Tracker API, exposing sub-APIs for stations, arrivals, train following, and +/// locations. +package com.cta4j.train; \ No newline at end of file diff --git a/src/main/java/com/cta4j/train/station/model/package-info.java b/src/main/java/com/cta4j/train/station/model/package-info.java new file mode 100644 index 00000000..7b78e5b2 --- /dev/null +++ b/src/main/java/com/cta4j/train/station/model/package-info.java @@ -0,0 +1,3 @@ +/// Domain model types returned by the CTA Train Tracker API's stations endpoint, representing a station, its +/// stops, and their addresses. +package com.cta4j.train.station.model; \ No newline at end of file diff --git a/src/main/java/com/cta4j/train/station/package-info.java b/src/main/java/com/cta4j/train/station/package-info.java new file mode 100644 index 00000000..f02868e4 --- /dev/null +++ b/src/main/java/com/cta4j/train/station/package-info.java @@ -0,0 +1,2 @@ +/// Retrieval of CTA train station and stop information. +package com.cta4j.train.station; \ No newline at end of file From 42138593261be29f133780a400bd6dc8f39a433c Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Thu, 30 Jul 2026 00:33:30 -0500 Subject: [PATCH 42/60] Javadoc consistency pass --- CLAUDE.md | 13 +++++++ src/main/java/com/cta4j/alert/AlertApi.java | 2 +- .../alert/common/model/AlertTrainLine.java | 2 +- .../detailedalert/model/ImpactedService.java | 4 +-- .../detailedalert/query/AlertsQuery.java | 8 ++--- .../query/BusRouteAlertsQuery.java | 8 ++--- .../detailedalert/query/LineAlertsQuery.java | 8 ++--- .../query/StationAlertsQuery.java | 8 ++--- .../alert/routestatus/RouteStatusApi.java | 36 +++++++++---------- .../exception/Cta4jRouteStatusException.java | 2 +- .../exception/RouteStatusErrorCode.java | 3 +- .../alert/routestatus/model/RouteStatus.java | 6 ++-- src/main/java/com/cta4j/bus/BusApi.java | 2 +- .../common/exception/Cta4jBusException.java | 2 +- .../java/com/cta4j/bus/detour/DetoursApi.java | 4 +-- .../com/cta4j/bus/detour/model/Detour.java | 4 +-- .../cta4j/bus/direction/DirectionsApi.java | 10 +++--- .../java/com/cta4j/bus/locale/LocalesApi.java | 4 +-- .../bus/locale/model/SupportedLocale.java | 2 +- .../com/cta4j/bus/pattern/PatternsApi.java | 4 +-- .../cta4j/bus/prediction/PredictionsApi.java | 2 +- .../bus/prediction/model/DynamicAction.java | 2 +- .../cta4j/bus/prediction/model/FlagStop.java | 2 +- .../bus/prediction/model/Prediction.java | 4 +-- .../query/StopPredictionsQuery.java | 8 ++--- .../query/VehiclePredictionsQuery.java | 8 ++--- .../java/com/cta4j/bus/route/RoutesApi.java | 2 +- .../java/com/cta4j/bus/stop/StopsApi.java | 4 +-- .../java/com/cta4j/bus/stop/model/Stop.java | 8 ++--- .../com/cta4j/bus/vehicle/VehiclesApi.java | 6 ++-- .../cta4j/bus/vehicle/model/TransitMode.java | 2 +- .../common/exception/Cta4jException.java | 10 +++--- .../com/cta4j/common/geo/Coordinates.java | 8 ++--- src/main/java/com/cta4j/train/TrainApi.java | 6 ++-- .../exception/Cta4jArrivalsException.java | 2 +- .../train/arrival/query/MapArrivalsQuery.java | 8 ++--- .../arrival/query/StopArrivalsQuery.java | 8 ++--- .../train/common/model/TrainDirection.java | 2 +- .../cta4j/train/common/model/TrainLine.java | 4 +-- .../com/cta4j/train/follow/FollowApi.java | 2 +- .../cta4j/train/follow/model/FollowTrain.java | 6 ++-- .../com/cta4j/train/follow/package-info.java | 2 +- .../cta4j/train/location/LocationsApi.java | 2 +- .../train/location/model/LocationTrain.java | 2 +- .../train/location/model/TrainLocations.java | 2 +- .../cta4j/train/location/package-info.java | 2 +- .../com/cta4j/train/station/StationsApi.java | 2 +- .../cta4j/train/station/model/Location.java | 4 +-- .../cta4j/train/station/model/Station.java | 4 +-- 49 files changed, 135 insertions(+), 121 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 887f650d..7fa7beaa 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -116,6 +116,19 @@ References: - **Summary sentence:** The first line is a standalone summary fragment ending in a period, third-person descriptive ("Returns the active arrivals for a station," not "This method returns..."). +- **Single-item accessor verb:** Always "Returns the X" — no exceptions. + This applies uniformly to every single-value accessor, including + wire-code accessors on domain-value enums (e.g. `getCode()` on + `TransitMode`, `DynamicAction`, `TrainLine`) and numeric/status-code + accessors on error-code enums or exceptions alike. Do not use "Gets the + X" to distinguish the two. +- **Builder setter `@return`:** Always backticked, "this `Builder` instance" — + never the unbacked "this builder instance" variant. +- **Builder creator methods:** One template for every builder, top-level + client builders and query-parameter builders alike — no terser variant. + The static `builder(...)` method: "Creates a new `Builder` for + constructing a/an `X`.", `@return` tag "a new `Builder`". The `build()` + method: "Builds a configured `X` instance.", `@return` tag "a new `X`". - **Tag order:** `@apiNote` → `@param` → `@return` → `@deprecated` → `@since` → `@throws` → `@see`. - **@param / @throws descriptions:** Lowercase phrase, no trailing period. diff --git a/src/main/java/com/cta4j/alert/AlertApi.java b/src/main/java/com/cta4j/alert/AlertApi.java index 7139e28f..f1112dd0 100644 --- a/src/main/java/com/cta4j/alert/AlertApi.java +++ b/src/main/java/com/cta4j/alert/AlertApi.java @@ -31,7 +31,7 @@ interface Builder { /// If not specified, the default CTA Alerts API host is used. /// /// @param host the API host - /// @return this builder instance + /// @return this `Builder` instance /// @throws NullPointerException if `host` is `null` Builder host(String host); diff --git a/src/main/java/com/cta4j/alert/common/model/AlertTrainLine.java b/src/main/java/com/cta4j/alert/common/model/AlertTrainLine.java index 130a9a41..ec48ebfc 100644 --- a/src/main/java/com/cta4j/alert/common/model/AlertTrainLine.java +++ b/src/main/java/com/cta4j/alert/common/model/AlertTrainLine.java @@ -44,7 +44,7 @@ public enum AlertTrainLine { this.code = Objects.requireNonNull(code); } - /// Gets the CTA Alerts API route designator for this train line. + /// Returns the CTA Alerts API route designator for this train line. /// /// @return the route designator public String getCode() { diff --git a/src/main/java/com/cta4j/alert/detailedalert/model/ImpactedService.java b/src/main/java/com/cta4j/alert/detailedalert/model/ImpactedService.java index 9dbc6937..7c287029 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/model/ImpactedService.java +++ b/src/main/java/com/cta4j/alert/detailedalert/model/ImpactedService.java @@ -14,7 +14,7 @@ /// @param name the name of this service (e.g., "Clark", "Red Line", "Jackson", "All Bus Routes") /// @param serviceId the identifier of this service; matches GTFS route or station IDs, except for systemwide /// groupings, which use a fixed identifier instead (e.g., "22", "Red", "Systemwide") -/// @param color the color of this service used in maps, as `rrggbb` (e.g., "565a5c") +/// @param color the color of this service used in maps; casing varies (e.g., "565a5c", "0065BD") /// @param textColor the suggested color of text displayed against `color`; casing varies (e.g., "ffffff", "FFFFFF") /// @param url the URL of this service's page on transitchicago.com @NullMarked @@ -34,7 +34,7 @@ public record ImpactedService( /// @param name the name of the service (e.g., "Clark", "Red Line", "Jackson", "All Bus Routes") /// @param serviceId the identifier of the service; matches GTFS route or station IDs, except for systemwide /// groupings, which use a fixed identifier instead (e.g., "22", "Red", "Systemwide") - /// @param color the color of the service used in maps, as `rrggbb` (e.g., "565a5c") + /// @param color the color of the service used in maps; casing varies (e.g., "565a5c", "0065BD") /// @param textColor the suggested color of text displayed against `color`; casing varies /// (e.g., "ffffff", "FFFFFF") /// @param url the URL of the service's page on transitchicago.com diff --git a/src/main/java/com/cta4j/alert/detailedalert/query/AlertsQuery.java b/src/main/java/com/cta4j/alert/detailedalert/query/AlertsQuery.java index 14d15439..362c9ab6 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/query/AlertsQuery.java +++ b/src/main/java/com/cta4j/alert/detailedalert/query/AlertsQuery.java @@ -41,9 +41,9 @@ public record AlertsQuery( } } - /// Creates a builder for `AlertsQuery`. + /// Creates a new `Builder` for constructing an `AlertsQuery`. /// - /// @return a new `Builder` instance + /// @return a new `Builder` public static Builder builder() { return new Builder(); } @@ -130,9 +130,9 @@ public Builder recentDays(int recentDays) { return this; } - /// Builds the `AlertsQuery`. + /// Builds a configured `AlertsQuery` instance. /// - /// @return a new `AlertsQuery` instance + /// @return a new `AlertsQuery` /// @throws IllegalArgumentException if both `byStartDate` and `recentDays` were specified public AlertsQuery build() { return new AlertsQuery( diff --git a/src/main/java/com/cta4j/alert/detailedalert/query/BusRouteAlertsQuery.java b/src/main/java/com/cta4j/alert/detailedalert/query/BusRouteAlertsQuery.java index e84ca349..c8afe3fd 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/query/BusRouteAlertsQuery.java +++ b/src/main/java/com/cta4j/alert/detailedalert/query/BusRouteAlertsQuery.java @@ -51,10 +51,10 @@ public record BusRouteAlertsQuery( } } - /// Creates a builder for `BusRouteAlertsQuery`. + /// Creates a new `Builder` for constructing a `BusRouteAlertsQuery`. /// /// @param routeIds the [Collection] of bus route IDs to retrieve alerts for - /// @return a new `Builder` instance + /// @return a new `Builder` /// @throws NullPointerException if `routeIds` is `null`, or if any element of `routeIds` is `null` public static Builder builder(Collection routeIds) { return new Builder(routeIds); @@ -150,9 +150,9 @@ public Builder recentDays(int recentDays) { return this; } - /// Builds the `BusRouteAlertsQuery`. + /// Builds a configured `BusRouteAlertsQuery` instance. /// - /// @return a new `BusRouteAlertsQuery` instance + /// @return a new `BusRouteAlertsQuery` /// @throws IllegalArgumentException if both `byStartDate` and `recentDays` were specified public BusRouteAlertsQuery build() { return new BusRouteAlertsQuery( diff --git a/src/main/java/com/cta4j/alert/detailedalert/query/LineAlertsQuery.java b/src/main/java/com/cta4j/alert/detailedalert/query/LineAlertsQuery.java index 57de75ad..d72f14db 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/query/LineAlertsQuery.java +++ b/src/main/java/com/cta4j/alert/detailedalert/query/LineAlertsQuery.java @@ -52,10 +52,10 @@ public record LineAlertsQuery( } } - /// Creates a builder for `LineAlertsQuery`. + /// Creates a new `Builder` for constructing a `LineAlertsQuery`. /// /// @param lines the [Collection] of [AlertTrainLine]s to retrieve alerts for - /// @return a new `Builder` instance + /// @return a new `Builder` /// @throws NullPointerException if `lines` is `null`, or if any element of `lines` is `null` public static Builder builder(Collection lines) { return new Builder(lines); @@ -151,9 +151,9 @@ public Builder recentDays(int recentDays) { return this; } - /// Builds the `LineAlertsQuery`. + /// Builds a configured `LineAlertsQuery` instance. /// - /// @return a new `LineAlertsQuery` instance + /// @return a new `LineAlertsQuery` /// @throws IllegalArgumentException if both `byStartDate` and `recentDays` were specified public LineAlertsQuery build() { return new LineAlertsQuery( diff --git a/src/main/java/com/cta4j/alert/detailedalert/query/StationAlertsQuery.java b/src/main/java/com/cta4j/alert/detailedalert/query/StationAlertsQuery.java index 943c8e08..89798108 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/query/StationAlertsQuery.java +++ b/src/main/java/com/cta4j/alert/detailedalert/query/StationAlertsQuery.java @@ -51,10 +51,10 @@ public record StationAlertsQuery( } } - /// Creates a builder for `StationAlertsQuery`. + /// Creates a new `Builder` for constructing a `StationAlertsQuery`. /// /// @param stationIds the [Collection] of train station IDs to retrieve alerts for - /// @return a new `Builder` instance + /// @return a new `Builder` /// @throws NullPointerException if `stationIds` is `null`, or if any element of `stationIds` is `null` public static Builder builder(Collection stationIds) { return new Builder(stationIds); @@ -150,9 +150,9 @@ public Builder recentDays(int recentDays) { return this; } - /// Builds the `StationAlertsQuery`. + /// Builds a configured `StationAlertsQuery` instance. /// - /// @return a new `StationAlertsQuery` instance + /// @return a new `StationAlertsQuery` /// @throws IllegalArgumentException if both `byStartDate` and `recentDays` were specified public StationAlertsQuery build() { return new StationAlertsQuery( diff --git a/src/main/java/com/cta4j/alert/routestatus/RouteStatusApi.java b/src/main/java/com/cta4j/alert/routestatus/RouteStatusApi.java index 3041484d..fee89f7f 100644 --- a/src/main/java/com/cta4j/alert/routestatus/RouteStatusApi.java +++ b/src/main/java/com/cta4j/alert/routestatus/RouteStatusApi.java @@ -27,7 +27,7 @@ public interface RouteStatusApi { /// @param types a [Collection] of service types /// @return a [List] of [RouteStatus]es corresponding to the provided types, or an empty [List] if no route /// statuses are found - /// @throws NullPointerException if `types` is `null` or contains `null` elements + /// @throws NullPointerException if `types` is `null`, or if any element of `types` is `null` /// @throws Cta4jRouteStatusException if the API returns an error response or the response cannot be parsed List findByTypes(Collection types); @@ -46,22 +46,22 @@ default List findByType(ServiceType type) { return this.findByTypes(types); } - /// Retrieves route statuses for the specified bus route IDs. + /// Retrieves route statuses by bus route IDs. /// /// @param routeIds a [Collection] of bus route IDs - /// @return a [List] of [RouteStatus]es associated with the bus route IDs, or an empty [List] if no route statuses - /// are found for the bus route IDs - /// @throws NullPointerException if `routeIds` is `null` or contains `null` elements + /// @return a [List] of [RouteStatus]es corresponding to the provided bus route IDs, or an empty [List] if no route + /// statuses are found for the bus route IDs + /// @throws NullPointerException if `routeIds` is `null`, or if any element of `routeIds` is `null` /// @throws IllegalArgumentException if any of the `routeIds` matches a train line code (e.g., "Red"); /// use [#findByLines(Collection)] instead /// @throws Cta4jRouteStatusException if the API returns an error response or the response cannot be parsed List findByBusRouteIds(Collection routeIds); - /// Retrieves route statuses for the specified bus route ID. + /// Retrieves route statuses by bus route ID. /// /// @param routeId the bus route ID - /// @return a [List] of [RouteStatus]es associated with the bus route ID, or an empty [List] if no route statuses - /// are found for the bus route ID + /// @return a [List] of [RouteStatus]es corresponding to the provided bus route ID, or an empty [List] if no route + /// statuses are found for the bus route ID /// @throws NullPointerException if `routeId` is `null` /// @throws IllegalArgumentException if `routeId` matches a train line code (e.g., "Red"); /// use [#findByLine(AlertTrainLine)] instead @@ -74,20 +74,20 @@ default List findByBusRouteId(String routeId) { return this.findByBusRouteIds(routeIds); } - /// Retrieves route statuses for the specified train lines. + /// Retrieves route statuses by train lines. /// /// @param lines a [Collection] of train lines - /// @return a [List] of [RouteStatus]es associated with the train lines, or an empty [List] if no route statuses - /// are found for the train lines - /// @throws NullPointerException if `lines` is `null` or contains `null` elements + /// @return a [List] of [RouteStatus]es corresponding to the provided train lines, or an empty [List] if no route + /// statuses are found for the train lines + /// @throws NullPointerException if `lines` is `null`, or if any element of `lines` is `null` /// @throws Cta4jRouteStatusException if the API returns an error response or the response cannot be parsed List findByLines(Collection lines); - /// Retrieves route statuses for the specified train line. + /// Retrieves route statuses by train line. /// /// @param line the train line - /// @return a [List] of [RouteStatus]es associated with the train line, or an empty [List] if no route statuses are - /// found for the train line + /// @return a [List] of [RouteStatus]es corresponding to the provided train line, or an empty [List] if no route + /// statuses are found for the train line /// @throws NullPointerException if `line` is `null` /// @throws Cta4jRouteStatusException if the API returns an error response or the response cannot be parsed default List findByLine(AlertTrainLine line) { @@ -98,11 +98,11 @@ default List findByLine(AlertTrainLine line) { return this.findByLines(lines); } - /// Retrieves route statuses for the specified station ID. + /// Retrieves route statuses by station ID. /// /// @param stationId the station ID - /// @return a [List] of [RouteStatus]es associated with the station ID, or an empty [List] if no route statuses are - /// found for the station ID + /// @return a [List] of [RouteStatus]es corresponding to the provided station ID, or an empty [List] if no route + /// statuses are found for the station ID /// @throws NullPointerException if `stationId` is `null` /// @throws Cta4jRouteStatusException if the API returns an error response or the response cannot be parsed List findByStationId(String stationId); diff --git a/src/main/java/com/cta4j/alert/routestatus/exception/Cta4jRouteStatusException.java b/src/main/java/com/cta4j/alert/routestatus/exception/Cta4jRouteStatusException.java index 0150235e..27c71aa7 100644 --- a/src/main/java/com/cta4j/alert/routestatus/exception/Cta4jRouteStatusException.java +++ b/src/main/java/com/cta4j/alert/routestatus/exception/Cta4jRouteStatusException.java @@ -5,7 +5,7 @@ import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; -/// A custom exception class for handling cta4j alert route status-specific errors. +/// A custom exception class for handling cta4j route status-specific errors. @NullMarked public final class Cta4jRouteStatusException extends Cta4jAlertException { @Nullable diff --git a/src/main/java/com/cta4j/alert/routestatus/exception/RouteStatusErrorCode.java b/src/main/java/com/cta4j/alert/routestatus/exception/RouteStatusErrorCode.java index 4afc22a1..f312ec05 100644 --- a/src/main/java/com/cta4j/alert/routestatus/exception/RouteStatusErrorCode.java +++ b/src/main/java/com/cta4j/alert/routestatus/exception/RouteStatusErrorCode.java @@ -10,8 +10,7 @@ public enum RouteStatusErrorCode { /// Indicates that no routes or stations matched the provided filter criteria. /// - /// This code is not documented in the CTA Alerts API documentation for the Route Status API, but has been observed - /// in practice. + /// This code is not documented in the CTA Route Status API documentation, but has been observed in practice. NO_RESULTS(50), /// Indicates that the provided station ID is not an integer. diff --git a/src/main/java/com/cta4j/alert/routestatus/model/RouteStatus.java b/src/main/java/com/cta4j/alert/routestatus/model/RouteStatus.java index 4b7e566c..3824480e 100644 --- a/src/main/java/com/cta4j/alert/routestatus/model/RouteStatus.java +++ b/src/main/java/com/cta4j/alert/routestatus/model/RouteStatus.java @@ -10,7 +10,8 @@ /// @param route the name of this route (e.g., "Clark") /// @param color the color of this route used in maps; casing varies (e.g., "565a5c", "0065BD") /// @param textColor the suggested color of text displayed against `color`; casing varies (e.g., "ffffff", "FFFFFF") -/// @param serviceId the unique GTFS route or station identifier of this route (e.g., "22") +/// @param serviceId the unique GTFS route or station identifier of this route (e.g., "22"); except for systemwide +/// groupings, which use a fixed identifier instead (e.g., "Systemwide") /// @param url the URL of this route's or station's page on transitchicago.com /// @param status the ultimate, human-readable status of this route /// (e.g., "Normal Service", "Service Change", "Bus Stop Note") @@ -32,7 +33,8 @@ public record RouteStatus( /// @param color the color of the route used in maps; casing varies (e.g., "565a5c", "0065BD") /// @param textColor the suggested color of text displayed against `color`; casing varies /// (e.g., "ffffff", "FFFFFF") - /// @param serviceId the unique GTFS route or station identifier of the route (e.g., "22") + /// @param serviceId the unique GTFS route or station identifier of the route (e.g., "22"); except for systemwide + /// groupings, which use a fixed identifier instead (e.g., "Systemwide") /// @param url the URL of the route's or station's page on transitchicago.com /// @param status the ultimate, human-readable status of the route /// (e.g., "Normal Service", "Service Change", "Bus Stop Note") diff --git a/src/main/java/com/cta4j/bus/BusApi.java b/src/main/java/com/cta4j/bus/BusApi.java index b8308bab..96962cd8 100644 --- a/src/main/java/com/cta4j/bus/BusApi.java +++ b/src/main/java/com/cta4j/bus/BusApi.java @@ -77,7 +77,7 @@ interface Builder { /// If not specified, the default CTA Bus Tracker API host is used. /// /// @param host the API host - /// @return this builder instance + /// @return this `Builder` instance /// @throws NullPointerException if `host` is `null` Builder host(String host); diff --git a/src/main/java/com/cta4j/bus/common/exception/Cta4jBusException.java b/src/main/java/com/cta4j/bus/common/exception/Cta4jBusException.java index a3442ae3..de5c2b30 100644 --- a/src/main/java/com/cta4j/bus/common/exception/Cta4jBusException.java +++ b/src/main/java/com/cta4j/bus/common/exception/Cta4jBusException.java @@ -33,7 +33,7 @@ public Cta4jBusException(String message, String endpoint, Throwable cause) { /// /// @param errors the list of [CtaError] objects /// @param endpoint the endpoint associated with the exception - /// @throws NullPointerException if `errors` or `endpoint` is `null`, or if `errors` contains `null` elements + /// @throws NullPointerException if `errors` or `endpoint` is `null`, or if any element of `errors` is `null` public Cta4jBusException(List errors, String endpoint) { super(joinMessages(errors), endpoint); } diff --git a/src/main/java/com/cta4j/bus/detour/DetoursApi.java b/src/main/java/com/cta4j/bus/detour/DetoursApi.java index 488d3442..47c2c108 100644 --- a/src/main/java/com/cta4j/bus/detour/DetoursApi.java +++ b/src/main/java/com/cta4j/bus/detour/DetoursApi.java @@ -6,7 +6,7 @@ import java.util.List; -/// Provides access to detour-related endpoints of the CTA BusTime API. +/// Provides access to detour-related endpoints of the CTA Bus Tracker API. /// /// This API allows retrieval of active service detours across all routes, or filtered by route and direction. @NullMarked @@ -29,7 +29,7 @@ public interface DetoursApi { /// Retrieves all active detours for the specified route ID and direction. /// /// @param routeId the route ID - /// @param direction the travel direction (e.g., "Northbound", "Southbound") + /// @param direction the direction (e.g., "Northbound", "Southbound") /// @return a [List] of [Detour]s associated with the route ID and direction, or an empty [List] if no detours are /// found for the route ID and direction /// @throws NullPointerException if `routeId` or `direction` is `null` diff --git a/src/main/java/com/cta4j/bus/detour/model/Detour.java b/src/main/java/com/cta4j/bus/detour/model/Detour.java index 4a3fd3f6..8a52f7e4 100644 --- a/src/main/java/com/cta4j/bus/detour/model/Detour.java +++ b/src/main/java/com/cta4j/bus/detour/model/Detour.java @@ -19,7 +19,7 @@ /// @param routeDirections the routes and directions affected by this detour /// @param startTime the time at which this detour begins /// @param endTime the time at which this detour ends -/// @param dataFeed the identifier for the data feed that supplied this detour, or `null` if not available +/// @param dataFeed the identifier for the data feed that supplied this detour, if applicable @NullMarked public record Detour( String id, @@ -40,7 +40,7 @@ public record Detour( /// @param routeDirections the routes and directions affected by the detour /// @param startTime the time at which the detour begins /// @param endTime the time at which the detour ends - /// @param dataFeed the identifier for the data feed that supplied the detour, or `null` if not available + /// @param dataFeed the identifier for the data feed that supplied the detour, if applicable /// @throws NullPointerException if `id`, `version`, `description`, `routeDirections`, `startTime`, or `endTime` is /// `null`, or if any element of `routeDirections` is `null` public Detour { diff --git a/src/main/java/com/cta4j/bus/direction/DirectionsApi.java b/src/main/java/com/cta4j/bus/direction/DirectionsApi.java index 33a312b3..ed5e098a 100644 --- a/src/main/java/com/cta4j/bus/direction/DirectionsApi.java +++ b/src/main/java/com/cta4j/bus/direction/DirectionsApi.java @@ -5,16 +5,16 @@ import java.util.List; -/// Provides access to direction-related endpoints of the CTA BusTime API. +/// Provides access to direction-related endpoints of the CTA Bus Tracker API. /// /// This API allows retrieval of available travel directions for a given route. @NullMarked public interface DirectionsApi { - /// Retrieves the available travel directions for the specified route (e.g., "Northbound", "Southbound"). + /// Retrieves the available travel directions for the specified route. /// - /// @param routeId the route identifier - /// @return a [List] of direction identifiers for the route, or an empty [List] if no directions are found for the - /// route + /// @param routeId the route ID + /// @return a [List] of direction identifiers for the route (e.g., "Northbound", "Southbound"), or an empty [List] + /// if no directions are found for the route /// @throws NullPointerException if `routeId` is `null` /// @throws Cta4jBusException if the API returns an error response or the response cannot be parsed List findByRouteId(String routeId); diff --git a/src/main/java/com/cta4j/bus/locale/LocalesApi.java b/src/main/java/com/cta4j/bus/locale/LocalesApi.java index 97b6ee1c..a18c5549 100644 --- a/src/main/java/com/cta4j/bus/locale/LocalesApi.java +++ b/src/main/java/com/cta4j/bus/locale/LocalesApi.java @@ -7,9 +7,9 @@ import java.util.List; import java.util.Locale; -/// Provides access to locale-related endpoints of the CTA BusTime API. +/// Provides access to locale-related endpoints of the CTA Bus Tracker API. /// -/// This API allows retrieval of supported locales for the CTA BusTime services. +/// This API allows retrieval of supported locales for the CTA Bus Tracker services. @NullMarked public interface LocalesApi { /// Retrieves the supported locales. diff --git a/src/main/java/com/cta4j/bus/locale/model/SupportedLocale.java b/src/main/java/com/cta4j/bus/locale/model/SupportedLocale.java index 9b8d1afb..7af9745c 100644 --- a/src/main/java/com/cta4j/bus/locale/model/SupportedLocale.java +++ b/src/main/java/com/cta4j/bus/locale/model/SupportedLocale.java @@ -5,7 +5,7 @@ import java.util.Locale; import java.util.Objects; -/// Represents a locale supported by the CTA Bus API. +/// Represents a locale supported by the CTA Bus Tracker API. /// /// @param locale the supported [Locale] /// @param displayName the human-readable name of this supported locale (e.g., "English", "Spanish") diff --git a/src/main/java/com/cta4j/bus/pattern/PatternsApi.java b/src/main/java/com/cta4j/bus/pattern/PatternsApi.java index 6944ce87..2c31da28 100644 --- a/src/main/java/com/cta4j/bus/pattern/PatternsApi.java +++ b/src/main/java/com/cta4j/bus/pattern/PatternsApi.java @@ -10,7 +10,7 @@ import java.util.Objects; import java.util.Optional; -/// Provides access to route pattern-related endpoints of the CTA BusTime API. +/// Provides access to route pattern-related endpoints of the CTA Bus Tracker API. /// /// This API allows retrieval of route patterns by their IDs or by associated route IDs. @NullMarked @@ -20,7 +20,7 @@ public interface PatternsApi { /// @param patternIds a [Collection] of route pattern IDs /// @return a [List] of [RoutePattern]s corresponding to the provided IDs, or an empty [List] if no patterns are /// found - /// @throws NullPointerException if `patternIds` is `null` or contains `null` elements + /// @throws NullPointerException if `patternIds` is `null`, or if any element of `patternIds` is `null` /// @throws IllegalArgumentException if more than 10 pattern IDs are provided /// @throws Cta4jBusException if the API returns an error response or the response cannot be parsed List findByIds(Collection patternIds); diff --git a/src/main/java/com/cta4j/bus/prediction/PredictionsApi.java b/src/main/java/com/cta4j/bus/prediction/PredictionsApi.java index a62018ff..75aa4062 100644 --- a/src/main/java/com/cta4j/bus/prediction/PredictionsApi.java +++ b/src/main/java/com/cta4j/bus/prediction/PredictionsApi.java @@ -10,7 +10,7 @@ import java.util.List; import java.util.Objects; -/// Provides access to prediction-related endpoints of the CTA BusTime API. +/// Provides access to prediction-related endpoints of the CTA Bus Tracker API. /// /// This API allows retrieval of predictions by stop IDs or vehicle IDs. @NullMarked diff --git a/src/main/java/com/cta4j/bus/prediction/model/DynamicAction.java b/src/main/java/com/cta4j/bus/prediction/model/DynamicAction.java index 743b912d..0acae5ff 100644 --- a/src/main/java/com/cta4j/bus/prediction/model/DynamicAction.java +++ b/src/main/java/com/cta4j/bus/prediction/model/DynamicAction.java @@ -69,7 +69,7 @@ public enum DynamicAction { this.code = code; } - /// Gets the code associated with this dynamic action. + /// Returns the code associated with this dynamic action. /// /// @return the dynamic action code public int getCode() { diff --git a/src/main/java/com/cta4j/bus/prediction/model/FlagStop.java b/src/main/java/com/cta4j/bus/prediction/model/FlagStop.java index 0eeb3748..5b0e36c6 100644 --- a/src/main/java/com/cta4j/bus/prediction/model/FlagStop.java +++ b/src/main/java/com/cta4j/bus/prediction/model/FlagStop.java @@ -23,7 +23,7 @@ public enum FlagStop { this.code = code; } - /// Gets the code associated with this flag-stop. + /// Returns the code associated with this flag-stop. /// /// @return the flag-stop code public int getCode() { diff --git a/src/main/java/com/cta4j/bus/prediction/model/Prediction.java b/src/main/java/com/cta4j/bus/prediction/model/Prediction.java index b91af267..c1b572c5 100644 --- a/src/main/java/com/cta4j/bus/prediction/model/Prediction.java +++ b/src/main/java/com/cta4j/bus/prediction/model/Prediction.java @@ -24,7 +24,7 @@ /// @param destination the final destination of the vehicle associated with this prediction /// @param arrivalTime the predicted date and time (UTC) of a vehicle’s arrival or departure to the stop associated /// with this prediction -/// @param delayed whether the vehicle associated with this prediction is currently delayed +/// @param delayed whether the vehicle associated with this prediction is currently delayed, if known /// @param metadata the metadata associated with this prediction @NullMarked public record Prediction( @@ -57,7 +57,7 @@ public record Prediction( /// @param destination the final destination of the vehicle associated with the prediction /// @param arrivalTime the predicted date and time (UTC) of a vehicle’s arrival or departure to the stop associated /// with the prediction - /// @param delayed whether the vehicle associated with the prediction is currently delayed + /// @param delayed whether the vehicle associated with the prediction is currently delayed, if known /// @param metadata the metadata associated with the prediction /// @throws NullPointerException if `predictionType`, `stopId`, `stopName`, `vehicleId`, `distanceToStop`, /// `routeId`, `routeDesignator`, `routeDirection`, `destination`, `arrivalTime`, or `metadata` is `null` diff --git a/src/main/java/com/cta4j/bus/prediction/query/StopPredictionsQuery.java b/src/main/java/com/cta4j/bus/prediction/query/StopPredictionsQuery.java index dd7ac792..98b4739f 100644 --- a/src/main/java/com/cta4j/bus/prediction/query/StopPredictionsQuery.java +++ b/src/main/java/com/cta4j/bus/prediction/query/StopPredictionsQuery.java @@ -43,10 +43,10 @@ public record StopPredictionsQuery( } } - /// Creates a builder for `StopPredictionsQuery`. + /// Creates a new `Builder` for constructing a `StopPredictionsQuery`. /// /// @param stopIds the [Collection] of stop IDs to retrieve predictions for - /// @return a new `Builder` instance + /// @return a new `Builder` /// @throws NullPointerException if `stopIds` is `null`, or if any element of `stopIds` is `null` public static Builder builder(Collection stopIds) { return new Builder(stopIds); @@ -100,9 +100,9 @@ public Builder maxResults(int maxResults) { return this; } - /// Builds the `StopPredictionsQuery`. + /// Builds a configured `StopPredictionsQuery` instance. /// - /// @return a new `StopPredictionsQuery` instance + /// @return a new `StopPredictionsQuery` /// @throws IllegalArgumentException if more than 10 stop IDs are provided public StopPredictionsQuery build() { return new StopPredictionsQuery( diff --git a/src/main/java/com/cta4j/bus/prediction/query/VehiclePredictionsQuery.java b/src/main/java/com/cta4j/bus/prediction/query/VehiclePredictionsQuery.java index f1910a97..2c5c6cac 100644 --- a/src/main/java/com/cta4j/bus/prediction/query/VehiclePredictionsQuery.java +++ b/src/main/java/com/cta4j/bus/prediction/query/VehiclePredictionsQuery.java @@ -36,10 +36,10 @@ public record VehiclePredictionsQuery( } } - /// Creates a builder for `VehiclePredictionsQuery`. + /// Creates a new `Builder` for constructing a `VehiclePredictionsQuery`. /// /// @param vehicleIds the [Collection] of vehicle IDs to retrieve predictions for - /// @return a new `Builder` instance + /// @return a new `Builder` /// @throws NullPointerException if `vehicleIds` is `null`, or if any element of `vehicleIds` is `null` public static Builder builder(Collection vehicleIds) { return new Builder(vehicleIds); @@ -77,9 +77,9 @@ public Builder maxResults(int maxResults) { return this; } - /// Builds the `VehiclePredictionsQuery`. + /// Builds a configured `VehiclePredictionsQuery` instance. /// - /// @return the constructed `VehiclePredictionsQuery` + /// @return a new `VehiclePredictionsQuery` /// @throws IllegalArgumentException if more than 10 vehicle IDs are provided public VehiclePredictionsQuery build() { return new VehiclePredictionsQuery( diff --git a/src/main/java/com/cta4j/bus/route/RoutesApi.java b/src/main/java/com/cta4j/bus/route/RoutesApi.java index 65fe1dc1..cbd2eca2 100644 --- a/src/main/java/com/cta4j/bus/route/RoutesApi.java +++ b/src/main/java/com/cta4j/bus/route/RoutesApi.java @@ -6,7 +6,7 @@ import java.util.List; -/// Provides access to route-related endpoints of the CTA BusTime API. +/// Provides access to route-related endpoints of the CTA Bus Tracker API. /// /// This API allows retrieval of all available routes. @NullMarked diff --git a/src/main/java/com/cta4j/bus/stop/StopsApi.java b/src/main/java/com/cta4j/bus/stop/StopsApi.java index 446dbe07..997acd3b 100644 --- a/src/main/java/com/cta4j/bus/stop/StopsApi.java +++ b/src/main/java/com/cta4j/bus/stop/StopsApi.java @@ -10,7 +10,7 @@ import java.util.Objects; import java.util.Optional; -/// Provides access to stop-related endpoints of the CTA BusTime API. +/// Provides access to stop-related endpoints of the CTA Bus Tracker API. /// /// This API allows retrieval of stops by route ID and direction, as well as by stop IDs. @NullMarked @@ -19,7 +19,7 @@ public interface StopsApi { /// /// @param stopIds a [Collection] of stop IDs /// @return a [List] of [Stop]s corresponding to the provided stop IDs, or an empty [List] if no stops are found - /// @throws NullPointerException if `stopIds` is `null` or contains `null` elements + /// @throws NullPointerException if `stopIds` is `null`, or if any element of `stopIds` is `null` /// @throws IllegalArgumentException if more than 10 stop IDs are provided /// @throws Cta4jBusException if the API returns an error response or the response cannot be parsed List findByIds(Collection stopIds); diff --git a/src/main/java/com/cta4j/bus/stop/model/Stop.java b/src/main/java/com/cta4j/bus/stop/model/Stop.java index 7fab9013..1b791abd 100644 --- a/src/main/java/com/cta4j/bus/stop/model/Stop.java +++ b/src/main/java/com/cta4j/bus/stop/model/Stop.java @@ -16,8 +16,8 @@ /// @param name the display name of this stop (e.g., "Clark & Addison") /// @param latitude the latitude coordinate of this stop /// @param longitude the longitude coordinate of this stop -/// @param detoursAdded the [List] of detour IDs which temporarily add service to this stop -/// @param detoursRemoved the [List] of detour IDs which temporarily remove service from this stop +/// @param detoursAdded the [List] of detour IDs which temporarily add service to this stop, if applicable +/// @param detoursRemoved the [List] of detour IDs which temporarily remove service from this stop, if applicable /// @param gtfsSequence the GTFS sequence number of this stop, if applicable /// @param adaAccessible whether this stop is ADA accessible, if known @NullMarked @@ -37,8 +37,8 @@ public record Stop( /// @param name the display name of the stop (e.g., "Clark & Addison") /// @param latitude the latitude coordinate of the stop /// @param longitude the longitude coordinate of the stop - /// @param detoursAdded the [List] of detour IDs which temporarily add service to the stop - /// @param detoursRemoved the [List] of detour IDs which temporarily remove service from the stop + /// @param detoursAdded the [List] of detour IDs which temporarily add service to the stop, if applicable + /// @param detoursRemoved the [List] of detour IDs which temporarily remove service from the stop, if applicable /// @param gtfsSequence the GTFS sequence number of the stop, if applicable /// @param adaAccessible whether the stop is ADA accessible, if known /// @throws NullPointerException if `id`, `name`, `latitude`, or `longitude` is `null`, or if any element of diff --git a/src/main/java/com/cta4j/bus/vehicle/VehiclesApi.java b/src/main/java/com/cta4j/bus/vehicle/VehiclesApi.java index b600fd55..c50b0a9c 100644 --- a/src/main/java/com/cta4j/bus/vehicle/VehiclesApi.java +++ b/src/main/java/com/cta4j/bus/vehicle/VehiclesApi.java @@ -10,7 +10,7 @@ import java.util.Objects; import java.util.Optional; -/// Provides access to vehicle-related endpoints of the CTA BusTime API. +/// Provides access to vehicle-related endpoints of the CTA Bus Tracker API. /// /// This API allows retrieval of vehicles by their IDs or by associated route IDs. @NullMarked @@ -19,7 +19,7 @@ public interface VehiclesApi { /// /// @param ids a [Collection] of vehicle IDs /// @return a [List] of [Vehicle]s corresponding to the provided IDs, or an empty [List] if no vehicles are found - /// @throws NullPointerException if `ids` is `null` or contains `null` elements + /// @throws NullPointerException if `ids` is `null`, or if any element of `ids` is `null` /// @throws IllegalArgumentException if more than 10 vehicle IDs are provided /// @throws Cta4jBusException if the API returns an error response or the response cannot be parsed List findByIds(Collection ids); @@ -62,7 +62,7 @@ default Optional findById(String id) { /// @param routeIds a [Collection] of route IDs /// @return a [List] of [Vehicle]s associated with the route IDs, or an empty [List] if no vehicles are found for /// the route IDs - /// @throws NullPointerException if `routeIds` is `null` or contains `null` elements + /// @throws NullPointerException if `routeIds` is `null`, or if any element of `routeIds` is `null` /// @throws IllegalArgumentException if more than 10 route IDs are provided /// @throws Cta4jBusException if the API returns an error response or the response cannot be parsed List findByRouteIds(Collection routeIds); diff --git a/src/main/java/com/cta4j/bus/vehicle/model/TransitMode.java b/src/main/java/com/cta4j/bus/vehicle/model/TransitMode.java index c93c35f5..d0465550 100644 --- a/src/main/java/com/cta4j/bus/vehicle/model/TransitMode.java +++ b/src/main/java/com/cta4j/bus/vehicle/model/TransitMode.java @@ -26,7 +26,7 @@ public enum TransitMode { this.code = code; } - /// Gets the code associated with this transit mode. + /// Returns the code associated with this transit mode. /// /// @return the transit mode code public int getCode() { diff --git a/src/main/java/com/cta4j/common/exception/Cta4jException.java b/src/main/java/com/cta4j/common/exception/Cta4jException.java index e2d5d3ed..bc0a6c28 100644 --- a/src/main/java/com/cta4j/common/exception/Cta4jException.java +++ b/src/main/java/com/cta4j/common/exception/Cta4jException.java @@ -4,7 +4,7 @@ import java.util.Objects; -/// A custom exception class for handling cta4j-specific errors. +/// A custom exception type for handling SDK-specific errors. @NullMarked public class Cta4jException extends RuntimeException { private final String endpoint; @@ -12,7 +12,7 @@ public class Cta4jException extends RuntimeException { /// Constructs a `Cta4jException`. /// /// @param message the detail message - /// @param endpoint the endpoint associated with the exception + /// @param endpoint the URL of the API endpoint associated with the exception /// @throws NullPointerException if `endpoint` is `null` public Cta4jException(String message, String endpoint) { super(message); @@ -23,7 +23,7 @@ public Cta4jException(String message, String endpoint) { /// Constructs a `Cta4jException`. /// /// @param message the detail message - /// @param endpoint the endpoint associated with the exception + /// @param endpoint the URL of the API endpoint associated with the exception /// @param cause the cause of the exception /// @throws NullPointerException if `endpoint` is `null` public Cta4jException(String message, String endpoint, Throwable cause) { @@ -32,9 +32,9 @@ public Cta4jException(String message, String endpoint, Throwable cause) { this.endpoint = Objects.requireNonNull(endpoint); } - /// Returns the endpoint associated with this exception. + /// Returns the URL of the API endpoint associated with this exception. /// - /// @return the endpoint + /// @return the endpoint URL public String getEndpoint() { return this.endpoint; } diff --git a/src/main/java/com/cta4j/common/geo/Coordinates.java b/src/main/java/com/cta4j/common/geo/Coordinates.java index 8f918d7b..36475017 100644 --- a/src/main/java/com/cta4j/common/geo/Coordinates.java +++ b/src/main/java/com/cta4j/common/geo/Coordinates.java @@ -8,8 +8,8 @@ /// Represents geographic coordinates. /// -/// @param latitude the latitude of these coordinates -/// @param longitude the longitude of these coordinates +/// @param latitude the latitude of these coordinates, in degrees (-90-90) +/// @param longitude the longitude of these coordinates, in degrees (-180-180) /// @param heading the heading of these coordinates in degrees (0-359) @NullMarked public record Coordinates( @@ -19,8 +19,8 @@ public record Coordinates( ) { /// Constructs a `Coordinates`. /// - /// @param latitude the latitude of the coordinates - /// @param longitude the longitude of the coordinates + /// @param latitude the latitude of the coordinates, in degrees (-90-90) + /// @param longitude the longitude of the coordinates, in degrees (-180-180) /// @param heading the heading of the coordinates in degrees (0-359) /// @throws NullPointerException if `latitude` or `longitude` is `null` /// @throws IllegalArgumentException if `latitude` is not between -90 and 90 (inclusive), `longitude` is not diff --git a/src/main/java/com/cta4j/train/TrainApi.java b/src/main/java/com/cta4j/train/TrainApi.java index e6341d3f..4b1562da 100644 --- a/src/main/java/com/cta4j/train/TrainApi.java +++ b/src/main/java/com/cta4j/train/TrainApi.java @@ -28,7 +28,7 @@ public interface TrainApi { /// @return the [ArrivalsApi] ArrivalsApi arrivals(); - /// Provides access to train follow-related endpoints. + /// Provides access to follow-related endpoints. /// /// @return the [FollowApi] FollowApi follow(); @@ -45,7 +45,7 @@ interface Builder { /// If not specified, the default CTA Train Tracker API host is used. /// /// @param host the API host - /// @return this builder instance + /// @return this `Builder` instance /// @throws NullPointerException if `host` is `null` Builder host(String host); @@ -54,7 +54,7 @@ interface Builder { /// If not specified, the default URL for station data is used. /// /// @param stationsUrl the URL for station data - /// @return this builder instance + /// @return this `Builder` instance /// @throws NullPointerException if `stationsUrl` is `null` Builder stationsUrl(String stationsUrl); diff --git a/src/main/java/com/cta4j/train/arrival/exception/Cta4jArrivalsException.java b/src/main/java/com/cta4j/train/arrival/exception/Cta4jArrivalsException.java index b88c896d..6e37b99f 100644 --- a/src/main/java/com/cta4j/train/arrival/exception/Cta4jArrivalsException.java +++ b/src/main/java/com/cta4j/train/arrival/exception/Cta4jArrivalsException.java @@ -33,7 +33,7 @@ public Cta4jArrivalsException(String message, int rawErrorCode) { /// Returns the error code associated with this exception, if available. /// - /// @return the error code, or `null` if not available + /// @return the error code, if available public @Nullable ArrivalsErrorCode getErrorCode() { return this.errorCode; } diff --git a/src/main/java/com/cta4j/train/arrival/query/MapArrivalsQuery.java b/src/main/java/com/cta4j/train/arrival/query/MapArrivalsQuery.java index af53c5b4..cb94f905 100644 --- a/src/main/java/com/cta4j/train/arrival/query/MapArrivalsQuery.java +++ b/src/main/java/com/cta4j/train/arrival/query/MapArrivalsQuery.java @@ -32,10 +32,10 @@ public record MapArrivalsQuery( } } - /// Creates a builder for `MapArrivalsQuery`. + /// Creates a new `Builder` for constructing a `MapArrivalsQuery`. /// /// @param mapId the ID of the map to retrieve arrivals for - /// @return a new `Builder` instance + /// @return a new `Builder` /// @throws NullPointerException if `mapId` is `null` public static Builder builder(String mapId) { return new Builder(mapId); @@ -85,9 +85,9 @@ public Builder maxResults(int maxResults) { return this; } - /// Builds the `MapArrivalsQuery`. + /// Builds a configured `MapArrivalsQuery` instance. /// - /// @return a new `MapArrivalsQuery` instance + /// @return a new `MapArrivalsQuery` public MapArrivalsQuery build() { return new MapArrivalsQuery( this.mapId, diff --git a/src/main/java/com/cta4j/train/arrival/query/StopArrivalsQuery.java b/src/main/java/com/cta4j/train/arrival/query/StopArrivalsQuery.java index a4526b69..d2400f2d 100644 --- a/src/main/java/com/cta4j/train/arrival/query/StopArrivalsQuery.java +++ b/src/main/java/com/cta4j/train/arrival/query/StopArrivalsQuery.java @@ -32,10 +32,10 @@ public record StopArrivalsQuery( } } - /// Creates a builder for `StopArrivalsQuery`. + /// Creates a new `Builder` for constructing a `StopArrivalsQuery`. /// /// @param stopId the ID of the stop to retrieve arrivals for - /// @return a new `Builder` instance + /// @return a new `Builder` /// @throws NullPointerException if `stopId` is `null` public static Builder builder(String stopId) { return new Builder(stopId); @@ -85,9 +85,9 @@ public Builder maxResults(int maxResults) { return this; } - /// Builds the `StopArrivalsQuery`. + /// Builds a configured `StopArrivalsQuery` instance. /// - /// @return a new `StopArrivalsQuery` instance + /// @return a new `StopArrivalsQuery` public StopArrivalsQuery build() { return new StopArrivalsQuery( this.stopId, diff --git a/src/main/java/com/cta4j/train/common/model/TrainDirection.java b/src/main/java/com/cta4j/train/common/model/TrainDirection.java index 479896f8..f68a0ee7 100644 --- a/src/main/java/com/cta4j/train/common/model/TrainDirection.java +++ b/src/main/java/com/cta4j/train/common/model/TrainDirection.java @@ -21,7 +21,7 @@ public enum TrainDirection { this.code = code; } - /// Gets the CTA direction code associated with this direction. + /// Returns the CTA direction code associated with this direction. /// /// @return the CTA direction code public int getCode() { diff --git a/src/main/java/com/cta4j/train/common/model/TrainLine.java b/src/main/java/com/cta4j/train/common/model/TrainLine.java index aa9c3cda..632484f0 100644 --- a/src/main/java/com/cta4j/train/common/model/TrainLine.java +++ b/src/main/java/com/cta4j/train/common/model/TrainLine.java @@ -39,14 +39,14 @@ public enum TrainLine { this.colorHex = Objects.requireNonNull(colorHex); } - /// Gets the CTA code for this train line. + /// Returns the CTA code for this train line. /// /// @return the CTA code public String getCode() { return this.code; } - /// Gets the hex color code of this train line. + /// Returns the hex color code of this train line. /// /// @return the hex color code public String getColorHex() { diff --git a/src/main/java/com/cta4j/train/follow/FollowApi.java b/src/main/java/com/cta4j/train/follow/FollowApi.java index aae1b0f6..29cfc585 100644 --- a/src/main/java/com/cta4j/train/follow/FollowApi.java +++ b/src/main/java/com/cta4j/train/follow/FollowApi.java @@ -8,7 +8,7 @@ /// Provides access to follow-related endpoints of the CTA Train Tracker API. /// -/// This API allows retrieval of information about a specific train run. +/// This API allows retrieval of upcoming predictions for a train by its run number. @NullMarked public interface FollowApi { /// Retrieves a train by its run number. diff --git a/src/main/java/com/cta4j/train/follow/model/FollowTrain.java b/src/main/java/com/cta4j/train/follow/model/FollowTrain.java index b754acf8..3dd3de00 100644 --- a/src/main/java/com/cta4j/train/follow/model/FollowTrain.java +++ b/src/main/java/com/cta4j/train/follow/model/FollowTrain.java @@ -10,7 +10,7 @@ /// Represents a response from the "follow" endpoint of the CTA Train Tracker API. /// -/// @param coordinates the current coordinates of this train being followed +/// @param coordinates the current coordinates of this train being followed, if applicable /// @param arrivals the [List] of [Arrival]s for this train being followed @NullMarked public record FollowTrain( @@ -19,9 +19,9 @@ public record FollowTrain( ) { /// Constructs a `FollowTrain`. /// - /// @param coordinates the current coordinates of the train being followed + /// @param coordinates the current coordinates of the train being followed, if applicable /// @param arrivals the [List] of [Arrival]s for the train being followed - /// @throws NullPointerException if `arrivals` is `null`, or if `arrivals` contains `null` elements + /// @throws NullPointerException if `arrivals` is `null`, or if any element of `arrivals` is `null` public FollowTrain { Objects.requireNonNull(arrivals); diff --git a/src/main/java/com/cta4j/train/follow/package-info.java b/src/main/java/com/cta4j/train/follow/package-info.java index 25a1c5a2..0cea91a7 100644 --- a/src/main/java/com/cta4j/train/follow/package-info.java +++ b/src/main/java/com/cta4j/train/follow/package-info.java @@ -1,2 +1,2 @@ -/// Retrieval of the upcoming predictions for a single CTA train run as it continues along its route. +/// Retrieval of upcoming predictions for a single CTA train run as it continues along its route. package com.cta4j.train.follow; \ No newline at end of file diff --git a/src/main/java/com/cta4j/train/location/LocationsApi.java b/src/main/java/com/cta4j/train/location/LocationsApi.java index 00a42134..d5a28f20 100644 --- a/src/main/java/com/cta4j/train/location/LocationsApi.java +++ b/src/main/java/com/cta4j/train/location/LocationsApi.java @@ -25,7 +25,7 @@ default List list() { /// @param lines a [List] of [TrainLine]s to filter the train locations by /// @return a [List] of [TrainLocations] corresponding to the provided lines, or an empty [List] if no train /// locations are found for the specified lines - /// @throws NullPointerException if `lines` is `null` or contains `null` elements + /// @throws NullPointerException if `lines` is `null`, or if any element of `lines` is `null` /// @throws Cta4jLocationsException if the API returns an error response or the response cannot be parsed List findByLines(List lines); diff --git a/src/main/java/com/cta4j/train/location/model/LocationTrain.java b/src/main/java/com/cta4j/train/location/model/LocationTrain.java index 97c68a96..9fc50163 100644 --- a/src/main/java/com/cta4j/train/location/model/LocationTrain.java +++ b/src/main/java/com/cta4j/train/location/model/LocationTrain.java @@ -11,7 +11,7 @@ /// Represents the location of a train on a route. /// /// @apiNote `flags` is not well-documented by the CTA. As such, its presence here is primarily for completeness and -/// may not be populated or described correctly +/// may not be populated or described correctly. /// /// @param run the run number of this train /// @param destinationStationId the unique identifier of the destination station for this train diff --git a/src/main/java/com/cta4j/train/location/model/TrainLocations.java b/src/main/java/com/cta4j/train/location/model/TrainLocations.java index cf23c9da..0729d028 100644 --- a/src/main/java/com/cta4j/train/location/model/TrainLocations.java +++ b/src/main/java/com/cta4j/train/location/model/TrainLocations.java @@ -19,7 +19,7 @@ public record TrainLocations( /// /// @param line the train line associated with the locations /// @param trains the [List] of [LocationTrain]s for the train line - /// @throws NullPointerException if `line` or `trains` is `null`, or if `trains` contains `null` elements + /// @throws NullPointerException if `line` or `trains` is `null`, or if any element of `trains` is `null` public TrainLocations { Objects.requireNonNull(line); Objects.requireNonNull(trains); diff --git a/src/main/java/com/cta4j/train/location/package-info.java b/src/main/java/com/cta4j/train/location/package-info.java index 9fefd7ff..d464dfac 100644 --- a/src/main/java/com/cta4j/train/location/package-info.java +++ b/src/main/java/com/cta4j/train/location/package-info.java @@ -1,2 +1,2 @@ -/// Retrieval of the real-time locations of CTA trains on a given route. +/// Retrieval of real-time locations of CTA trains on a given route. package com.cta4j.train.location; \ No newline at end of file diff --git a/src/main/java/com/cta4j/train/station/StationsApi.java b/src/main/java/com/cta4j/train/station/StationsApi.java index 82248160..eb460459 100644 --- a/src/main/java/com/cta4j/train/station/StationsApi.java +++ b/src/main/java/com/cta4j/train/station/StationsApi.java @@ -6,7 +6,7 @@ import java.util.List; -/// Provides access to station-related endpoints. +/// Provides access to station-related endpoints of the CTA Train Tracker API. /// /// This API allows retrieval of station information, including station names, IDs, and other details. /// diff --git a/src/main/java/com/cta4j/train/station/model/Location.java b/src/main/java/com/cta4j/train/station/model/Location.java index 18beb8c4..26f9c746 100644 --- a/src/main/java/com/cta4j/train/station/model/Location.java +++ b/src/main/java/com/cta4j/train/station/model/Location.java @@ -10,7 +10,7 @@ /// /// @param latitude the latitude /// @param longitude the longitude -/// @param humanAddress the human-readable address, or `null` if not available +/// @param humanAddress the human-readable address, if applicable @NullMarked public record Location( BigDecimal latitude, @@ -21,7 +21,7 @@ public record Location( /// /// @param latitude the latitude /// @param longitude the longitude - /// @param humanAddress the human-readable address, or `null` if not available + /// @param humanAddress the human-readable address, if applicable /// @throws NullPointerException if `latitude` or `longitude` is `null` public Location { Objects.requireNonNull(latitude); diff --git a/src/main/java/com/cta4j/train/station/model/Station.java b/src/main/java/com/cta4j/train/station/model/Station.java index 301314a0..410ded3c 100644 --- a/src/main/java/com/cta4j/train/station/model/Station.java +++ b/src/main/java/com/cta4j/train/station/model/Station.java @@ -13,7 +13,7 @@ /// @param stopName the stop name of this station /// @param name the name of this station /// @param descriptiveName the descriptive name of this station -/// @param mapId the map identifier of this station +/// @param mapId the unique map identifier of this station /// @param adaAccessible whether this station is ADA accessible /// @param lines the [Set] of [TrainLine]s that serve this station /// @param location the [Location] of this station @@ -36,7 +36,7 @@ public record Station( /// @param stopName the stop name of the station /// @param name the name of the station /// @param descriptiveName the descriptive name of the station - /// @param mapId the map identifier of the station + /// @param mapId the unique map identifier of the station /// @param adaAccessible whether the station is ADA accessible /// @param lines the [Set] of [TrainLine]s that serve the station /// @param location the [Location] of the station From 3918fff76d6923fb5e7078ee48521fb644508b8c Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Thu, 30 Jul 2026 17:08:56 -0500 Subject: [PATCH 43/60] package-info.java newlines --- .../java/com/cta4j/alert/common/exception/package-info.java | 2 +- src/main/java/com/cta4j/alert/common/model/package-info.java | 2 +- src/main/java/com/cta4j/alert/common/package-info.java | 2 +- .../com/cta4j/alert/detailedalert/exception/package-info.java | 2 +- .../java/com/cta4j/alert/detailedalert/model/package-info.java | 2 +- src/main/java/com/cta4j/alert/detailedalert/package-info.java | 2 +- .../java/com/cta4j/alert/detailedalert/query/package-info.java | 2 +- src/main/java/com/cta4j/alert/package-info.java | 2 +- .../com/cta4j/alert/routestatus/exception/package-info.java | 2 +- .../java/com/cta4j/alert/routestatus/model/package-info.java | 2 +- src/main/java/com/cta4j/alert/routestatus/package-info.java | 2 +- src/main/java/com/cta4j/bus/common/exception/package-info.java | 2 +- src/main/java/com/cta4j/bus/common/package-info.java | 2 +- src/main/java/com/cta4j/bus/detour/model/package-info.java | 2 +- src/main/java/com/cta4j/bus/detour/package-info.java | 2 +- src/main/java/com/cta4j/bus/direction/package-info.java | 2 +- src/main/java/com/cta4j/bus/locale/model/package-info.java | 2 +- src/main/java/com/cta4j/bus/locale/package-info.java | 2 +- src/main/java/com/cta4j/bus/package-info.java | 2 +- src/main/java/com/cta4j/bus/pattern/model/package-info.java | 2 +- src/main/java/com/cta4j/bus/pattern/package-info.java | 2 +- src/main/java/com/cta4j/bus/prediction/model/package-info.java | 2 +- src/main/java/com/cta4j/bus/prediction/package-info.java | 2 +- src/main/java/com/cta4j/bus/prediction/query/package-info.java | 2 +- src/main/java/com/cta4j/bus/route/model/package-info.java | 2 +- src/main/java/com/cta4j/bus/route/package-info.java | 2 +- src/main/java/com/cta4j/bus/stop/model/package-info.java | 2 +- src/main/java/com/cta4j/bus/stop/package-info.java | 2 +- src/main/java/com/cta4j/bus/vehicle/model/package-info.java | 2 +- src/main/java/com/cta4j/bus/vehicle/package-info.java | 2 +- src/main/java/com/cta4j/common/exception/package-info.java | 2 +- src/main/java/com/cta4j/common/geo/package-info.java | 2 +- src/main/java/com/cta4j/common/package-info.java | 2 +- .../java/com/cta4j/train/arrival/exception/package-info.java | 2 +- src/main/java/com/cta4j/train/arrival/package-info.java | 2 +- src/main/java/com/cta4j/train/arrival/query/package-info.java | 2 +- .../java/com/cta4j/train/common/exception/package-info.java | 2 +- src/main/java/com/cta4j/train/common/model/package-info.java | 2 +- src/main/java/com/cta4j/train/common/package-info.java | 2 +- .../java/com/cta4j/train/follow/exception/package-info.java | 2 +- src/main/java/com/cta4j/train/follow/model/package-info.java | 2 +- src/main/java/com/cta4j/train/follow/package-info.java | 2 +- .../java/com/cta4j/train/location/exception/package-info.java | 2 +- src/main/java/com/cta4j/train/location/model/package-info.java | 2 +- src/main/java/com/cta4j/train/location/package-info.java | 2 +- src/main/java/com/cta4j/train/package-info.java | 2 +- src/main/java/com/cta4j/train/station/model/package-info.java | 2 +- src/main/java/com/cta4j/train/station/package-info.java | 2 +- 48 files changed, 48 insertions(+), 48 deletions(-) diff --git a/src/main/java/com/cta4j/alert/common/exception/package-info.java b/src/main/java/com/cta4j/alert/common/exception/package-info.java index cd9f763d..8d8a2f27 100644 --- a/src/main/java/com/cta4j/alert/common/exception/package-info.java +++ b/src/main/java/com/cta4j/alert/common/exception/package-info.java @@ -1,2 +1,2 @@ /// Base exception type shared across the CTA Alerts API's detailed alert and route status sub-APIs. -package com.cta4j.alert.common.exception; \ No newline at end of file +package com.cta4j.alert.common.exception; diff --git a/src/main/java/com/cta4j/alert/common/model/package-info.java b/src/main/java/com/cta4j/alert/common/model/package-info.java index 1c8a9133..e34a133f 100644 --- a/src/main/java/com/cta4j/alert/common/model/package-info.java +++ b/src/main/java/com/cta4j/alert/common/model/package-info.java @@ -1,3 +1,3 @@ /// Domain model types shared across the CTA Alerts API's detailed alert and route status sub-APIs, such as train /// line and service type designators. -package com.cta4j.alert.common.model; \ No newline at end of file +package com.cta4j.alert.common.model; diff --git a/src/main/java/com/cta4j/alert/common/package-info.java b/src/main/java/com/cta4j/alert/common/package-info.java index 2881a266..17f23843 100644 --- a/src/main/java/com/cta4j/alert/common/package-info.java +++ b/src/main/java/com/cta4j/alert/common/package-info.java @@ -1,3 +1,3 @@ /// Shared types, configuration, and internal plumbing used across the CTA Alerts API's detailed alert and route /// status sub-APIs. -package com.cta4j.alert.common; \ No newline at end of file +package com.cta4j.alert.common; diff --git a/src/main/java/com/cta4j/alert/detailedalert/exception/package-info.java b/src/main/java/com/cta4j/alert/detailedalert/exception/package-info.java index 92c2b444..d8ffa051 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/exception/package-info.java +++ b/src/main/java/com/cta4j/alert/detailedalert/exception/package-info.java @@ -1,2 +1,2 @@ /// Exception type and error code enum thrown by the CTA Detailed Alerts API. -package com.cta4j.alert.detailedalert.exception; \ No newline at end of file +package com.cta4j.alert.detailedalert.exception; diff --git a/src/main/java/com/cta4j/alert/detailedalert/model/package-info.java b/src/main/java/com/cta4j/alert/detailedalert/model/package-info.java index 2df035c8..08cc23b1 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/model/package-info.java +++ b/src/main/java/com/cta4j/alert/detailedalert/model/package-info.java @@ -1,2 +1,2 @@ /// Domain model types returned by the CTA Detailed Alerts API, representing alerts and their impacted services. -package com.cta4j.alert.detailedalert.model; \ No newline at end of file +package com.cta4j.alert.detailedalert.model; diff --git a/src/main/java/com/cta4j/alert/detailedalert/package-info.java b/src/main/java/com/cta4j/alert/detailedalert/package-info.java index 44af4cb9..3f26fd01 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/package-info.java +++ b/src/main/java/com/cta4j/alert/detailedalert/package-info.java @@ -1,2 +1,2 @@ /// Retrieval of detailed CTA service alerts, filterable by bus route ID, train line, or station ID. -package com.cta4j.alert.detailedalert; \ No newline at end of file +package com.cta4j.alert.detailedalert; diff --git a/src/main/java/com/cta4j/alert/detailedalert/query/package-info.java b/src/main/java/com/cta4j/alert/detailedalert/query/package-info.java index 22c6f3ce..df63160d 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/query/package-info.java +++ b/src/main/java/com/cta4j/alert/detailedalert/query/package-info.java @@ -1,3 +1,3 @@ /// Query builder types for filtering requests to the CTA Detailed Alerts API by bus route ID, train line, or /// station ID. -package com.cta4j.alert.detailedalert.query; \ No newline at end of file +package com.cta4j.alert.detailedalert.query; diff --git a/src/main/java/com/cta4j/alert/package-info.java b/src/main/java/com/cta4j/alert/package-info.java index 9c6d6727..f647b72a 100644 --- a/src/main/java/com/cta4j/alert/package-info.java +++ b/src/main/java/com/cta4j/alert/package-info.java @@ -1,2 +1,2 @@ /// Entry point for the CTA Alerts API, exposing sub-APIs for detailed alerts and route status information. -package com.cta4j.alert; \ No newline at end of file +package com.cta4j.alert; diff --git a/src/main/java/com/cta4j/alert/routestatus/exception/package-info.java b/src/main/java/com/cta4j/alert/routestatus/exception/package-info.java index 88ff4667..6838a0cc 100644 --- a/src/main/java/com/cta4j/alert/routestatus/exception/package-info.java +++ b/src/main/java/com/cta4j/alert/routestatus/exception/package-info.java @@ -1,2 +1,2 @@ /// Exception type and error code enum thrown by the CTA Route Status API. -package com.cta4j.alert.routestatus.exception; \ No newline at end of file +package com.cta4j.alert.routestatus.exception; diff --git a/src/main/java/com/cta4j/alert/routestatus/model/package-info.java b/src/main/java/com/cta4j/alert/routestatus/model/package-info.java index 65345459..665b0f8b 100644 --- a/src/main/java/com/cta4j/alert/routestatus/model/package-info.java +++ b/src/main/java/com/cta4j/alert/routestatus/model/package-info.java @@ -1,2 +1,2 @@ /// Domain model type returned by the CTA Route Status API, representing the status of a single route. -package com.cta4j.alert.routestatus.model; \ No newline at end of file +package com.cta4j.alert.routestatus.model; diff --git a/src/main/java/com/cta4j/alert/routestatus/package-info.java b/src/main/java/com/cta4j/alert/routestatus/package-info.java index ad99ad2e..8a6026a8 100644 --- a/src/main/java/com/cta4j/alert/routestatus/package-info.java +++ b/src/main/java/com/cta4j/alert/routestatus/package-info.java @@ -1,3 +1,3 @@ /// Retrieval of the current status of CTA bus and train routes, filterable by service type, bus route ID, train /// line, or station ID. -package com.cta4j.alert.routestatus; \ No newline at end of file +package com.cta4j.alert.routestatus; diff --git a/src/main/java/com/cta4j/bus/common/exception/package-info.java b/src/main/java/com/cta4j/bus/common/exception/package-info.java index b76a9e5a..e68a8690 100644 --- a/src/main/java/com/cta4j/bus/common/exception/package-info.java +++ b/src/main/java/com/cta4j/bus/common/exception/package-info.java @@ -1,2 +1,2 @@ /// Base exception type thrown by the CTA Bus Tracker API's sub-APIs. -package com.cta4j.bus.common.exception; \ No newline at end of file +package com.cta4j.bus.common.exception; diff --git a/src/main/java/com/cta4j/bus/common/package-info.java b/src/main/java/com/cta4j/bus/common/package-info.java index c97d4ae0..449b9860 100644 --- a/src/main/java/com/cta4j/bus/common/package-info.java +++ b/src/main/java/com/cta4j/bus/common/package-info.java @@ -1,3 +1,3 @@ /// Shared types, configuration, and internal plumbing used across the CTA Bus Tracker API's sub-APIs, including the /// error-handling utilities common to bustime-response endpoints. -package com.cta4j.bus.common; \ No newline at end of file +package com.cta4j.bus.common; diff --git a/src/main/java/com/cta4j/bus/detour/model/package-info.java b/src/main/java/com/cta4j/bus/detour/model/package-info.java index 14be41a5..ee17388b 100644 --- a/src/main/java/com/cta4j/bus/detour/model/package-info.java +++ b/src/main/java/com/cta4j/bus/detour/model/package-info.java @@ -1,3 +1,3 @@ /// Domain model types returned by the CTA Bus Tracker API's detours endpoint, representing an active detour and /// the route directions it affects. -package com.cta4j.bus.detour.model; \ No newline at end of file +package com.cta4j.bus.detour.model; diff --git a/src/main/java/com/cta4j/bus/detour/package-info.java b/src/main/java/com/cta4j/bus/detour/package-info.java index 614df10b..96a027b7 100644 --- a/src/main/java/com/cta4j/bus/detour/package-info.java +++ b/src/main/java/com/cta4j/bus/detour/package-info.java @@ -1,2 +1,2 @@ /// Retrieval of active CTA bus detours and the route directions they affect. -package com.cta4j.bus.detour; \ No newline at end of file +package com.cta4j.bus.detour; diff --git a/src/main/java/com/cta4j/bus/direction/package-info.java b/src/main/java/com/cta4j/bus/direction/package-info.java index f9ba1e6b..534faf6f 100644 --- a/src/main/java/com/cta4j/bus/direction/package-info.java +++ b/src/main/java/com/cta4j/bus/direction/package-info.java @@ -1,2 +1,2 @@ /// Retrieval of the available travel directions for a given CTA bus route. -package com.cta4j.bus.direction; \ No newline at end of file +package com.cta4j.bus.direction; diff --git a/src/main/java/com/cta4j/bus/locale/model/package-info.java b/src/main/java/com/cta4j/bus/locale/model/package-info.java index 7e1a37e5..c7060a61 100644 --- a/src/main/java/com/cta4j/bus/locale/model/package-info.java +++ b/src/main/java/com/cta4j/bus/locale/model/package-info.java @@ -1,2 +1,2 @@ /// Domain model type returned by the CTA Bus Tracker API's locales endpoint, representing a supported locale. -package com.cta4j.bus.locale.model; \ No newline at end of file +package com.cta4j.bus.locale.model; diff --git a/src/main/java/com/cta4j/bus/locale/package-info.java b/src/main/java/com/cta4j/bus/locale/package-info.java index bad13c3a..fc4dbf67 100644 --- a/src/main/java/com/cta4j/bus/locale/package-info.java +++ b/src/main/java/com/cta4j/bus/locale/package-info.java @@ -1,2 +1,2 @@ /// Retrieval of the locales supported by the CTA Bus Tracker API. -package com.cta4j.bus.locale; \ No newline at end of file +package com.cta4j.bus.locale; diff --git a/src/main/java/com/cta4j/bus/package-info.java b/src/main/java/com/cta4j/bus/package-info.java index 94286cb9..5b8300e1 100644 --- a/src/main/java/com/cta4j/bus/package-info.java +++ b/src/main/java/com/cta4j/bus/package-info.java @@ -1,3 +1,3 @@ /// Entry point for the CTA Bus Tracker API, exposing sub-APIs for vehicles, routes, directions, stops, patterns, /// predictions, locales, detours, and system time. -package com.cta4j.bus; \ No newline at end of file +package com.cta4j.bus; diff --git a/src/main/java/com/cta4j/bus/pattern/model/package-info.java b/src/main/java/com/cta4j/bus/pattern/model/package-info.java index bd21f33c..31ee17b6 100644 --- a/src/main/java/com/cta4j/bus/pattern/model/package-info.java +++ b/src/main/java/com/cta4j/bus/pattern/model/package-info.java @@ -1,3 +1,3 @@ /// Domain model types returned by the CTA Bus Tracker API's patterns endpoint, representing a route pattern and /// its constituent points. -package com.cta4j.bus.pattern.model; \ No newline at end of file +package com.cta4j.bus.pattern.model; diff --git a/src/main/java/com/cta4j/bus/pattern/package-info.java b/src/main/java/com/cta4j/bus/pattern/package-info.java index d969e18d..fe64d53b 100644 --- a/src/main/java/com/cta4j/bus/pattern/package-info.java +++ b/src/main/java/com/cta4j/bus/pattern/package-info.java @@ -1,2 +1,2 @@ /// Retrieval of CTA bus route patterns, describing the sequence of points a route follows. -package com.cta4j.bus.pattern; \ No newline at end of file +package com.cta4j.bus.pattern; diff --git a/src/main/java/com/cta4j/bus/prediction/model/package-info.java b/src/main/java/com/cta4j/bus/prediction/model/package-info.java index f2bf4010..4ed5fdff 100644 --- a/src/main/java/com/cta4j/bus/prediction/model/package-info.java +++ b/src/main/java/com/cta4j/bus/prediction/model/package-info.java @@ -1,3 +1,3 @@ /// Domain model types returned by the CTA Bus Tracker API's predictions endpoint, representing an arrival /// prediction and its metadata. -package com.cta4j.bus.prediction.model; \ No newline at end of file +package com.cta4j.bus.prediction.model; diff --git a/src/main/java/com/cta4j/bus/prediction/package-info.java b/src/main/java/com/cta4j/bus/prediction/package-info.java index db08688e..98f9e102 100644 --- a/src/main/java/com/cta4j/bus/prediction/package-info.java +++ b/src/main/java/com/cta4j/bus/prediction/package-info.java @@ -1,2 +1,2 @@ /// Retrieval of real-time CTA bus arrival predictions for stops and vehicles. -package com.cta4j.bus.prediction; \ No newline at end of file +package com.cta4j.bus.prediction; diff --git a/src/main/java/com/cta4j/bus/prediction/query/package-info.java b/src/main/java/com/cta4j/bus/prediction/query/package-info.java index af6be0b9..c8543bb9 100644 --- a/src/main/java/com/cta4j/bus/prediction/query/package-info.java +++ b/src/main/java/com/cta4j/bus/prediction/query/package-info.java @@ -1,3 +1,3 @@ /// Query builder types for filtering requests to the CTA Bus Tracker API's predictions endpoint by stop or /// vehicle. -package com.cta4j.bus.prediction.query; \ No newline at end of file +package com.cta4j.bus.prediction.query; diff --git a/src/main/java/com/cta4j/bus/route/model/package-info.java b/src/main/java/com/cta4j/bus/route/model/package-info.java index 1162125c..8969a002 100644 --- a/src/main/java/com/cta4j/bus/route/model/package-info.java +++ b/src/main/java/com/cta4j/bus/route/model/package-info.java @@ -1,2 +1,2 @@ /// Domain model type returned by the CTA Bus Tracker API's routes endpoint, representing a single bus route. -package com.cta4j.bus.route.model; \ No newline at end of file +package com.cta4j.bus.route.model; diff --git a/src/main/java/com/cta4j/bus/route/package-info.java b/src/main/java/com/cta4j/bus/route/package-info.java index fc15a6c4..3928147d 100644 --- a/src/main/java/com/cta4j/bus/route/package-info.java +++ b/src/main/java/com/cta4j/bus/route/package-info.java @@ -1,2 +1,2 @@ /// Retrieval of all available CTA bus routes. -package com.cta4j.bus.route; \ No newline at end of file +package com.cta4j.bus.route; diff --git a/src/main/java/com/cta4j/bus/stop/model/package-info.java b/src/main/java/com/cta4j/bus/stop/model/package-info.java index 952a06db..ba99b479 100644 --- a/src/main/java/com/cta4j/bus/stop/model/package-info.java +++ b/src/main/java/com/cta4j/bus/stop/model/package-info.java @@ -1,2 +1,2 @@ /// Domain model type returned by the CTA Bus Tracker API's stops endpoint, representing a single bus stop. -package com.cta4j.bus.stop.model; \ No newline at end of file +package com.cta4j.bus.stop.model; diff --git a/src/main/java/com/cta4j/bus/stop/package-info.java b/src/main/java/com/cta4j/bus/stop/package-info.java index 5bb265b4..555609f1 100644 --- a/src/main/java/com/cta4j/bus/stop/package-info.java +++ b/src/main/java/com/cta4j/bus/stop/package-info.java @@ -1,2 +1,2 @@ /// Retrieval of CTA bus stops for a given route and direction. -package com.cta4j.bus.stop; \ No newline at end of file +package com.cta4j.bus.stop; diff --git a/src/main/java/com/cta4j/bus/vehicle/model/package-info.java b/src/main/java/com/cta4j/bus/vehicle/model/package-info.java index 77ba2559..985d5c7e 100644 --- a/src/main/java/com/cta4j/bus/vehicle/model/package-info.java +++ b/src/main/java/com/cta4j/bus/vehicle/model/package-info.java @@ -1,3 +1,3 @@ /// Domain model types returned by the CTA Bus Tracker API's vehicles endpoint, representing a vehicle and its /// metadata. -package com.cta4j.bus.vehicle.model; \ No newline at end of file +package com.cta4j.bus.vehicle.model; diff --git a/src/main/java/com/cta4j/bus/vehicle/package-info.java b/src/main/java/com/cta4j/bus/vehicle/package-info.java index c505a053..c277200c 100644 --- a/src/main/java/com/cta4j/bus/vehicle/package-info.java +++ b/src/main/java/com/cta4j/bus/vehicle/package-info.java @@ -1,2 +1,2 @@ /// Retrieval of real-time CTA bus vehicle locations and metadata. -package com.cta4j.bus.vehicle; \ No newline at end of file +package com.cta4j.bus.vehicle; diff --git a/src/main/java/com/cta4j/common/exception/package-info.java b/src/main/java/com/cta4j/common/exception/package-info.java index 0da83f07..10b06600 100644 --- a/src/main/java/com/cta4j/common/exception/package-info.java +++ b/src/main/java/com/cta4j/common/exception/package-info.java @@ -1,2 +1,2 @@ /// Base exception type for the SDK, extended by every transit-specific exception type. -package com.cta4j.common.exception; \ No newline at end of file +package com.cta4j.common.exception; diff --git a/src/main/java/com/cta4j/common/geo/package-info.java b/src/main/java/com/cta4j/common/geo/package-info.java index ec082a94..000353fe 100644 --- a/src/main/java/com/cta4j/common/geo/package-info.java +++ b/src/main/java/com/cta4j/common/geo/package-info.java @@ -1,2 +1,2 @@ /// Domain model type representing geographic coordinates, shared across the bus and train APIs. -package com.cta4j.common.geo; \ No newline at end of file +package com.cta4j.common.geo; diff --git a/src/main/java/com/cta4j/common/package-info.java b/src/main/java/com/cta4j/common/package-info.java index 0b3bf438..3090aa49 100644 --- a/src/main/java/com/cta4j/common/package-info.java +++ b/src/main/java/com/cta4j/common/package-info.java @@ -1,3 +1,3 @@ /// Cross-cutting types shared across the bus and train APIs, such as geographic coordinates and the base SDK /// exception type. -package com.cta4j.common; \ No newline at end of file +package com.cta4j.common; diff --git a/src/main/java/com/cta4j/train/arrival/exception/package-info.java b/src/main/java/com/cta4j/train/arrival/exception/package-info.java index 6a08ac2f..9a499fdd 100644 --- a/src/main/java/com/cta4j/train/arrival/exception/package-info.java +++ b/src/main/java/com/cta4j/train/arrival/exception/package-info.java @@ -1,2 +1,2 @@ /// Exception type and error code enum thrown by the CTA Train Tracker API's arrivals endpoint. -package com.cta4j.train.arrival.exception; \ No newline at end of file +package com.cta4j.train.arrival.exception; diff --git a/src/main/java/com/cta4j/train/arrival/package-info.java b/src/main/java/com/cta4j/train/arrival/package-info.java index 2d5441c1..4d1a51a7 100644 --- a/src/main/java/com/cta4j/train/arrival/package-info.java +++ b/src/main/java/com/cta4j/train/arrival/package-info.java @@ -1,2 +1,2 @@ /// Retrieval of real-time CTA train arrival predictions for stations and stops. -package com.cta4j.train.arrival; \ No newline at end of file +package com.cta4j.train.arrival; diff --git a/src/main/java/com/cta4j/train/arrival/query/package-info.java b/src/main/java/com/cta4j/train/arrival/query/package-info.java index d6208882..c312a12f 100644 --- a/src/main/java/com/cta4j/train/arrival/query/package-info.java +++ b/src/main/java/com/cta4j/train/arrival/query/package-info.java @@ -1,3 +1,3 @@ /// Query builder types for filtering requests to the CTA Train Tracker API's arrivals endpoint by station or /// stop. -package com.cta4j.train.arrival.query; \ No newline at end of file +package com.cta4j.train.arrival.query; diff --git a/src/main/java/com/cta4j/train/common/exception/package-info.java b/src/main/java/com/cta4j/train/common/exception/package-info.java index fd9e3bea..51341afa 100644 --- a/src/main/java/com/cta4j/train/common/exception/package-info.java +++ b/src/main/java/com/cta4j/train/common/exception/package-info.java @@ -1,2 +1,2 @@ /// Base exception type thrown by the CTA Train Tracker API's sub-APIs. -package com.cta4j.train.common.exception; \ No newline at end of file +package com.cta4j.train.common.exception; diff --git a/src/main/java/com/cta4j/train/common/model/package-info.java b/src/main/java/com/cta4j/train/common/model/package-info.java index fda8c5a7..c0baf256 100644 --- a/src/main/java/com/cta4j/train/common/model/package-info.java +++ b/src/main/java/com/cta4j/train/common/model/package-info.java @@ -1,3 +1,3 @@ /// Domain model types shared across the CTA Train Tracker API's sub-APIs, such as arrivals and train line and /// direction designators. -package com.cta4j.train.common.model; \ No newline at end of file +package com.cta4j.train.common.model; diff --git a/src/main/java/com/cta4j/train/common/package-info.java b/src/main/java/com/cta4j/train/common/package-info.java index cf11408c..716d1f48 100644 --- a/src/main/java/com/cta4j/train/common/package-info.java +++ b/src/main/java/com/cta4j/train/common/package-info.java @@ -1,2 +1,2 @@ /// Shared types, configuration, and internal plumbing used across the CTA Train Tracker API's sub-APIs. -package com.cta4j.train.common; \ No newline at end of file +package com.cta4j.train.common; diff --git a/src/main/java/com/cta4j/train/follow/exception/package-info.java b/src/main/java/com/cta4j/train/follow/exception/package-info.java index 4babce45..a432f5a9 100644 --- a/src/main/java/com/cta4j/train/follow/exception/package-info.java +++ b/src/main/java/com/cta4j/train/follow/exception/package-info.java @@ -1,2 +1,2 @@ /// Exception type and error code enum thrown by the CTA Train Tracker API's follow endpoint. -package com.cta4j.train.follow.exception; \ No newline at end of file +package com.cta4j.train.follow.exception; diff --git a/src/main/java/com/cta4j/train/follow/model/package-info.java b/src/main/java/com/cta4j/train/follow/model/package-info.java index 7e8cbb08..aaef2a65 100644 --- a/src/main/java/com/cta4j/train/follow/model/package-info.java +++ b/src/main/java/com/cta4j/train/follow/model/package-info.java @@ -1,3 +1,3 @@ /// Domain model type returned by the CTA Train Tracker API's follow endpoint, representing a train's upcoming /// predictions. -package com.cta4j.train.follow.model; \ No newline at end of file +package com.cta4j.train.follow.model; diff --git a/src/main/java/com/cta4j/train/follow/package-info.java b/src/main/java/com/cta4j/train/follow/package-info.java index 0cea91a7..57b63ffd 100644 --- a/src/main/java/com/cta4j/train/follow/package-info.java +++ b/src/main/java/com/cta4j/train/follow/package-info.java @@ -1,2 +1,2 @@ /// Retrieval of upcoming predictions for a single CTA train run as it continues along its route. -package com.cta4j.train.follow; \ No newline at end of file +package com.cta4j.train.follow; diff --git a/src/main/java/com/cta4j/train/location/exception/package-info.java b/src/main/java/com/cta4j/train/location/exception/package-info.java index 513f865d..429a10f6 100644 --- a/src/main/java/com/cta4j/train/location/exception/package-info.java +++ b/src/main/java/com/cta4j/train/location/exception/package-info.java @@ -1,2 +1,2 @@ /// Exception type and error code enum thrown by the CTA Train Tracker API's locations endpoint. -package com.cta4j.train.location.exception; \ No newline at end of file +package com.cta4j.train.location.exception; diff --git a/src/main/java/com/cta4j/train/location/model/package-info.java b/src/main/java/com/cta4j/train/location/model/package-info.java index 8ea2f922..e4f1445e 100644 --- a/src/main/java/com/cta4j/train/location/model/package-info.java +++ b/src/main/java/com/cta4j/train/location/model/package-info.java @@ -1,3 +1,3 @@ /// Domain model types returned by the CTA Train Tracker API's locations endpoint, representing a train's /// location and its containing collection. -package com.cta4j.train.location.model; \ No newline at end of file +package com.cta4j.train.location.model; diff --git a/src/main/java/com/cta4j/train/location/package-info.java b/src/main/java/com/cta4j/train/location/package-info.java index d464dfac..b3821847 100644 --- a/src/main/java/com/cta4j/train/location/package-info.java +++ b/src/main/java/com/cta4j/train/location/package-info.java @@ -1,2 +1,2 @@ /// Retrieval of real-time locations of CTA trains on a given route. -package com.cta4j.train.location; \ No newline at end of file +package com.cta4j.train.location; diff --git a/src/main/java/com/cta4j/train/package-info.java b/src/main/java/com/cta4j/train/package-info.java index 5570e9ca..77be0018 100644 --- a/src/main/java/com/cta4j/train/package-info.java +++ b/src/main/java/com/cta4j/train/package-info.java @@ -1,3 +1,3 @@ /// Entry point for the CTA Train Tracker API, exposing sub-APIs for stations, arrivals, train following, and /// locations. -package com.cta4j.train; \ No newline at end of file +package com.cta4j.train; diff --git a/src/main/java/com/cta4j/train/station/model/package-info.java b/src/main/java/com/cta4j/train/station/model/package-info.java index 7b78e5b2..6eb8617c 100644 --- a/src/main/java/com/cta4j/train/station/model/package-info.java +++ b/src/main/java/com/cta4j/train/station/model/package-info.java @@ -1,3 +1,3 @@ /// Domain model types returned by the CTA Train Tracker API's stations endpoint, representing a station, its /// stops, and their addresses. -package com.cta4j.train.station.model; \ No newline at end of file +package com.cta4j.train.station.model; diff --git a/src/main/java/com/cta4j/train/station/package-info.java b/src/main/java/com/cta4j/train/station/package-info.java index f02868e4..57d7014d 100644 --- a/src/main/java/com/cta4j/train/station/package-info.java +++ b/src/main/java/com/cta4j/train/station/package-info.java @@ -1,2 +1,2 @@ /// Retrieval of CTA train station and stop information. -package com.cta4j.train.station; \ No newline at end of file +package com.cta4j.train.station; From 4840fdd8db5a884d4dce94781bed97b8415c7100 Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Fri, 31 Jul 2026 16:01:20 -0500 Subject: [PATCH 44/60] API consistency review --- .../detailedalert/query/AlertsQuery.java | 6 +----- .../query/BusRouteAlertsQuery.java | 9 +-------- .../detailedalert/query/LineAlertsQuery.java | 9 +-------- .../query/StationAlertsQuery.java | 9 +-------- .../util/{ApiUtils.java => BusApiUtils.java} | 6 +++--- .../detour/internal/impl/DetoursApiImpl.java | 4 ++-- .../internal/impl/DirectionsApiImpl.java | 4 ++-- .../locale/internal/impl/LocalesApiImpl.java | 4 ++-- .../internal/impl/PatternsApiImpl.java | 6 +++--- .../internal/impl/PredictionsApiImpl.java | 4 ++-- .../query/StopPredictionsQuery.java | 10 +++------- .../query/VehiclePredictionsQuery.java | 10 +++------- .../route/internal/impl/RoutesApiImpl.java | 4 ++-- .../bus/stop/internal/impl/StopsApiImpl.java | 6 +++--- .../internal/impl/VehiclesApiImpl.java | 8 ++++---- .../train/arrival/query/MapArrivalsQuery.java | 6 +----- .../arrival/query/StopArrivalsQuery.java | 6 +----- ...ApiUtilsTest.java => BusApiUtilsTest.java} | 20 +++++++++---------- 18 files changed, 45 insertions(+), 86 deletions(-) rename src/main/java/com/cta4j/bus/common/internal/util/{ApiUtils.java => BusApiUtils.java} (91%) rename src/test/java/com/cta4j/bus/common/internal/util/{ApiUtilsTest.java => BusApiUtilsTest.java} (67%) diff --git a/src/main/java/com/cta4j/alert/detailedalert/query/AlertsQuery.java b/src/main/java/com/cta4j/alert/detailedalert/query/AlertsQuery.java index 362c9ab6..17c07a4a 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/query/AlertsQuery.java +++ b/src/main/java/com/cta4j/alert/detailedalert/query/AlertsQuery.java @@ -62,11 +62,7 @@ public static final class Builder { @Nullable private Integer recentDays; - /// Constructs a `Builder`. - /// - /// By default, `activeOnly` is `false`, and `accessibility` and `planned` are `true`, matching the CTA Alerts - /// API's own defaults. - public Builder() { + private Builder() { this.activeOnly = false; this.accessibility = true; this.planned = true; diff --git a/src/main/java/com/cta4j/alert/detailedalert/query/BusRouteAlertsQuery.java b/src/main/java/com/cta4j/alert/detailedalert/query/BusRouteAlertsQuery.java index c8afe3fd..7bd6b655 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/query/BusRouteAlertsQuery.java +++ b/src/main/java/com/cta4j/alert/detailedalert/query/BusRouteAlertsQuery.java @@ -76,14 +76,7 @@ public static final class Builder { @Nullable private Integer recentDays; - /// Constructs a `Builder`. - /// - /// By default, `activeOnly` is `false`, and `accessibility` and `planned` are `true`, matching the CTA Alerts - /// API's own defaults. - /// - /// @param routeIds the [Collection] of bus route IDs to retrieve alerts for - /// @throws NullPointerException if `routeIds` is `null`, or if any element of `routeIds` is `null` - public Builder(Collection routeIds) { + private Builder(Collection routeIds) { Objects.requireNonNull(routeIds); this.routeIds = List.copyOf(routeIds); diff --git a/src/main/java/com/cta4j/alert/detailedalert/query/LineAlertsQuery.java b/src/main/java/com/cta4j/alert/detailedalert/query/LineAlertsQuery.java index d72f14db..1998859e 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/query/LineAlertsQuery.java +++ b/src/main/java/com/cta4j/alert/detailedalert/query/LineAlertsQuery.java @@ -77,14 +77,7 @@ public static final class Builder { @Nullable private Integer recentDays; - /// Constructs a `Builder`. - /// - /// By default, `activeOnly` is `false`, and `accessibility` and `planned` are `true`, matching the CTA Alerts - /// API's own defaults. - /// - /// @param lines the [Collection] of [AlertTrainLine]s to retrieve alerts for - /// @throws NullPointerException if `lines` is `null`, or if any element of `lines` is `null` - public Builder(Collection lines) { + private Builder(Collection lines) { Objects.requireNonNull(lines); this.lines = List.copyOf(lines); diff --git a/src/main/java/com/cta4j/alert/detailedalert/query/StationAlertsQuery.java b/src/main/java/com/cta4j/alert/detailedalert/query/StationAlertsQuery.java index 89798108..a598a8a1 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/query/StationAlertsQuery.java +++ b/src/main/java/com/cta4j/alert/detailedalert/query/StationAlertsQuery.java @@ -76,14 +76,7 @@ public static final class Builder { @Nullable private Integer recentDays; - /// Constructs a `Builder`. - /// - /// By default, `activeOnly` is `false`, and `accessibility` and `planned` are `true`, matching the CTA Alerts - /// API's own defaults. - /// - /// @param stationIds the [Collection] of train station IDs to retrieve alerts for - /// @throws NullPointerException if `stationIds` is `null`, or if any element of `stationIds` is `null` - public Builder(Collection stationIds) { + private Builder(Collection stationIds) { Objects.requireNonNull(stationIds); this.stationIds = List.copyOf(stationIds); diff --git a/src/main/java/com/cta4j/bus/common/internal/util/ApiUtils.java b/src/main/java/com/cta4j/bus/common/internal/util/BusApiUtils.java similarity index 91% rename from src/main/java/com/cta4j/bus/common/internal/util/ApiUtils.java rename to src/main/java/com/cta4j/bus/common/internal/util/BusApiUtils.java index dc079c14..0442c371 100644 --- a/src/main/java/com/cta4j/bus/common/internal/util/ApiUtils.java +++ b/src/main/java/com/cta4j/bus/common/internal/util/BusApiUtils.java @@ -14,12 +14,12 @@ @ApiStatus.Internal @NullMarked -public final class ApiUtils { - private static final Logger log = LoggerFactory.getLogger(ApiUtils.class); +public final class BusApiUtils { + private static final Logger log = LoggerFactory.getLogger(BusApiUtils.class); public static final int MAX_IDS_PER_REQUEST = 10; - private ApiUtils() { + private BusApiUtils() { throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); } diff --git a/src/main/java/com/cta4j/bus/detour/internal/impl/DetoursApiImpl.java b/src/main/java/com/cta4j/bus/detour/internal/impl/DetoursApiImpl.java index 695be1be..9f20bd4c 100644 --- a/src/main/java/com/cta4j/bus/detour/internal/impl/DetoursApiImpl.java +++ b/src/main/java/com/cta4j/bus/detour/internal/impl/DetoursApiImpl.java @@ -2,7 +2,7 @@ import com.cta4j.bus.common.exception.Cta4jBusException; import com.cta4j.bus.common.internal.config.BusApiConfig; -import com.cta4j.bus.common.internal.util.ApiUtils; +import com.cta4j.bus.common.internal.util.BusApiUtils; import com.cta4j.bus.common.internal.util.BusApiConstants; import com.cta4j.bus.common.internal.wire.CtaResponse; import com.cta4j.bus.detour.DetoursApi; @@ -119,7 +119,7 @@ private List makeRequest(String url) { .toList(); } - ApiUtils.checkErrors(errors, BusApiConstants.DETOURS_ENDPOINT); + BusApiUtils.checkErrors(errors, BusApiConstants.DETOURS_ENDPOINT); return List.of(); } diff --git a/src/main/java/com/cta4j/bus/direction/internal/impl/DirectionsApiImpl.java b/src/main/java/com/cta4j/bus/direction/internal/impl/DirectionsApiImpl.java index 696781e6..053902fb 100644 --- a/src/main/java/com/cta4j/bus/direction/internal/impl/DirectionsApiImpl.java +++ b/src/main/java/com/cta4j/bus/direction/internal/impl/DirectionsApiImpl.java @@ -2,7 +2,7 @@ import com.cta4j.bus.common.exception.Cta4jBusException; import com.cta4j.bus.common.internal.config.BusApiConfig; -import com.cta4j.bus.common.internal.util.ApiUtils; +import com.cta4j.bus.common.internal.util.BusApiUtils; import com.cta4j.bus.common.internal.util.BusApiConstants; import com.cta4j.bus.common.internal.wire.CtaResponse; import com.cta4j.bus.direction.DirectionsApi; @@ -80,7 +80,7 @@ public List findByRouteId(String routeId) { .toList(); } - ApiUtils.checkErrors(errors, BusApiConstants.DIRECTIONS_ENDPOINT); + BusApiUtils.checkErrors(errors, BusApiConstants.DIRECTIONS_ENDPOINT); return List.of(); } diff --git a/src/main/java/com/cta4j/bus/locale/internal/impl/LocalesApiImpl.java b/src/main/java/com/cta4j/bus/locale/internal/impl/LocalesApiImpl.java index de0375e7..faece1e9 100644 --- a/src/main/java/com/cta4j/bus/locale/internal/impl/LocalesApiImpl.java +++ b/src/main/java/com/cta4j/bus/locale/internal/impl/LocalesApiImpl.java @@ -9,7 +9,7 @@ import com.cta4j.bus.locale.internal.wire.CtaLocale; import com.cta4j.bus.locale.internal.wire.CtaLocaleBustimeResponse; import com.cta4j.bus.locale.internal.wire.CtaLocaleError; -import com.cta4j.bus.common.internal.util.ApiUtils; +import com.cta4j.bus.common.internal.util.BusApiUtils; import com.cta4j.bus.locale.model.SupportedLocale; import org.apache.hc.client5.http.fluent.Request; import org.apache.hc.core5.net.URIBuilder; @@ -118,7 +118,7 @@ private List makeRequest(String url) { .toList(); } - ApiUtils.checkErrors(errors, BusApiConstants.LOCALES_ENDPOINT); + BusApiUtils.checkErrors(errors, BusApiConstants.LOCALES_ENDPOINT); return List.of(); } diff --git a/src/main/java/com/cta4j/bus/pattern/internal/impl/PatternsApiImpl.java b/src/main/java/com/cta4j/bus/pattern/internal/impl/PatternsApiImpl.java index 2e0ff7d4..f428f8da 100644 --- a/src/main/java/com/cta4j/bus/pattern/internal/impl/PatternsApiImpl.java +++ b/src/main/java/com/cta4j/bus/pattern/internal/impl/PatternsApiImpl.java @@ -2,7 +2,7 @@ import com.cta4j.bus.common.exception.Cta4jBusException; import com.cta4j.bus.common.internal.config.BusApiConfig; -import com.cta4j.bus.common.internal.util.ApiUtils; +import com.cta4j.bus.common.internal.util.BusApiUtils; import com.cta4j.bus.common.internal.util.BusApiConstants; import com.cta4j.bus.common.internal.wire.CtaResponse; import com.cta4j.bus.pattern.PatternsApi; @@ -46,7 +46,7 @@ public List findByIds(Collection patternIds) { return List.of(); } - ApiUtils.requireMaxIds(patternIds, "pattern"); + BusApiUtils.requireMaxIds(patternIds, "pattern"); String patternIdsString = String.join(",", patternIds); @@ -114,7 +114,7 @@ private List makeRequest(String url) { .toList(); } - ApiUtils.checkErrors(errors, BusApiConstants.PATTERNS_ENDPOINT); + BusApiUtils.checkErrors(errors, BusApiConstants.PATTERNS_ENDPOINT); return List.of(); } diff --git a/src/main/java/com/cta4j/bus/prediction/internal/impl/PredictionsApiImpl.java b/src/main/java/com/cta4j/bus/prediction/internal/impl/PredictionsApiImpl.java index 7ec479ba..f8845c0c 100644 --- a/src/main/java/com/cta4j/bus/prediction/internal/impl/PredictionsApiImpl.java +++ b/src/main/java/com/cta4j/bus/prediction/internal/impl/PredictionsApiImpl.java @@ -2,7 +2,7 @@ import com.cta4j.bus.common.exception.Cta4jBusException; import com.cta4j.bus.common.internal.config.BusApiConfig; -import com.cta4j.bus.common.internal.util.ApiUtils; +import com.cta4j.bus.common.internal.util.BusApiUtils; import com.cta4j.bus.common.internal.util.BusApiConstants; import com.cta4j.bus.common.internal.wire.CtaResponse; import com.cta4j.bus.prediction.PredictionsApi; @@ -143,7 +143,7 @@ private List makeRequest(String url) { .toList(); } - ApiUtils.checkErrors(errors, BusApiConstants.PREDICTIONS_ENDPOINT); + BusApiUtils.checkErrors(errors, BusApiConstants.PREDICTIONS_ENDPOINT); return List.of(); } diff --git a/src/main/java/com/cta4j/bus/prediction/query/StopPredictionsQuery.java b/src/main/java/com/cta4j/bus/prediction/query/StopPredictionsQuery.java index 98b4739f..f55d35bb 100644 --- a/src/main/java/com/cta4j/bus/prediction/query/StopPredictionsQuery.java +++ b/src/main/java/com/cta4j/bus/prediction/query/StopPredictionsQuery.java @@ -1,6 +1,6 @@ package com.cta4j.bus.prediction.query; -import com.cta4j.bus.common.internal.util.ApiUtils; +import com.cta4j.bus.common.internal.util.BusApiUtils; import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; @@ -30,7 +30,7 @@ public record StopPredictionsQuery( public StopPredictionsQuery { Objects.requireNonNull(stopIds); - ApiUtils.requireMaxIds(stopIds, "stop"); + BusApiUtils.requireMaxIds(stopIds, "stop"); stopIds = List.copyOf(stopIds); @@ -62,11 +62,7 @@ public static final class Builder { @Nullable private Integer maxResults; - /// Constructs a `Builder`. - /// - /// @param stopIds the [Collection] of stop IDs to retrieve predictions for - /// @throws NullPointerException if `stopIds` is `null`, or if any element of `stopIds` is `null` - public Builder(Collection stopIds) { + private Builder(Collection stopIds) { Objects.requireNonNull(stopIds); this.stopIds = List.copyOf(stopIds); diff --git a/src/main/java/com/cta4j/bus/prediction/query/VehiclePredictionsQuery.java b/src/main/java/com/cta4j/bus/prediction/query/VehiclePredictionsQuery.java index 2c5c6cac..c242e162 100644 --- a/src/main/java/com/cta4j/bus/prediction/query/VehiclePredictionsQuery.java +++ b/src/main/java/com/cta4j/bus/prediction/query/VehiclePredictionsQuery.java @@ -1,6 +1,6 @@ package com.cta4j.bus.prediction.query; -import com.cta4j.bus.common.internal.util.ApiUtils; +import com.cta4j.bus.common.internal.util.BusApiUtils; import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; @@ -27,7 +27,7 @@ public record VehiclePredictionsQuery( public VehiclePredictionsQuery { Objects.requireNonNull(vehicleIds); - ApiUtils.requireMaxIds(vehicleIds, "vehicle"); + BusApiUtils.requireMaxIds(vehicleIds, "vehicle"); vehicleIds = List.copyOf(vehicleIds); @@ -52,11 +52,7 @@ public static final class Builder { @Nullable private Integer maxResults; - /// Constructs a `Builder`. - /// - /// @param vehicleIds the [Collection] of vehicle IDs to retrieve predictions for - /// @throws NullPointerException if `vehicleIds` is `null`, or if any element of `vehicleIds` is `null` - public Builder(Collection vehicleIds) { + private Builder(Collection vehicleIds) { Objects.requireNonNull(vehicleIds); this.vehicleIds = List.copyOf(vehicleIds); diff --git a/src/main/java/com/cta4j/bus/route/internal/impl/RoutesApiImpl.java b/src/main/java/com/cta4j/bus/route/internal/impl/RoutesApiImpl.java index 2bb7144a..4052c7b3 100644 --- a/src/main/java/com/cta4j/bus/route/internal/impl/RoutesApiImpl.java +++ b/src/main/java/com/cta4j/bus/route/internal/impl/RoutesApiImpl.java @@ -2,7 +2,7 @@ import com.cta4j.bus.common.exception.Cta4jBusException; import com.cta4j.bus.common.internal.config.BusApiConfig; -import com.cta4j.bus.common.internal.util.ApiUtils; +import com.cta4j.bus.common.internal.util.BusApiUtils; import com.cta4j.bus.common.internal.util.BusApiConstants; import com.cta4j.bus.common.internal.wire.CtaResponse; import com.cta4j.bus.route.RoutesApi; @@ -80,7 +80,7 @@ public List list() { .toList(); } - ApiUtils.checkErrors(errors, BusApiConstants.ROUTES_ENDPOINT); + BusApiUtils.checkErrors(errors, BusApiConstants.ROUTES_ENDPOINT); return List.of(); } diff --git a/src/main/java/com/cta4j/bus/stop/internal/impl/StopsApiImpl.java b/src/main/java/com/cta4j/bus/stop/internal/impl/StopsApiImpl.java index 1ea4f406..c2e56cf6 100644 --- a/src/main/java/com/cta4j/bus/stop/internal/impl/StopsApiImpl.java +++ b/src/main/java/com/cta4j/bus/stop/internal/impl/StopsApiImpl.java @@ -2,7 +2,7 @@ import com.cta4j.bus.common.exception.Cta4jBusException; import com.cta4j.bus.common.internal.config.BusApiConfig; -import com.cta4j.bus.common.internal.util.ApiUtils; +import com.cta4j.bus.common.internal.util.BusApiUtils; import com.cta4j.bus.common.internal.util.BusApiConstants; import com.cta4j.bus.common.internal.wire.CtaResponse; import com.cta4j.bus.stop.StopsApi; @@ -64,7 +64,7 @@ public List findByIds(Collection stopIds) { return List.of(); } - ApiUtils.requireMaxIds(stopIds, "stop"); + BusApiUtils.requireMaxIds(stopIds, "stop"); String stopIdsString = String.join(",", stopIds); @@ -115,7 +115,7 @@ private List makeRequest(String url) { .toList(); } - ApiUtils.checkErrors(errors, BusApiConstants.STOPS_ENDPOINT); + BusApiUtils.checkErrors(errors, BusApiConstants.STOPS_ENDPOINT); return List.of(); } diff --git a/src/main/java/com/cta4j/bus/vehicle/internal/impl/VehiclesApiImpl.java b/src/main/java/com/cta4j/bus/vehicle/internal/impl/VehiclesApiImpl.java index 1ab6dc57..5d0ccf19 100644 --- a/src/main/java/com/cta4j/bus/vehicle/internal/impl/VehiclesApiImpl.java +++ b/src/main/java/com/cta4j/bus/vehicle/internal/impl/VehiclesApiImpl.java @@ -2,7 +2,7 @@ import com.cta4j.bus.common.exception.Cta4jBusException; import com.cta4j.bus.common.internal.config.BusApiConfig; -import com.cta4j.bus.common.internal.util.ApiUtils; +import com.cta4j.bus.common.internal.util.BusApiUtils; import com.cta4j.bus.common.internal.util.BusApiConstants; import com.cta4j.bus.common.internal.wire.CtaResponse; import com.cta4j.bus.vehicle.VehiclesApi; @@ -46,7 +46,7 @@ public List findByIds(Collection ids) { return List.of(); } - ApiUtils.requireMaxIds(ids, "vehicle"); + BusApiUtils.requireMaxIds(ids, "vehicle"); String idsString = String.join(",", ids); @@ -74,7 +74,7 @@ public List findByRouteIds(Collection routeIds) { return List.of(); } - ApiUtils.requireMaxIds(routeIds, "route"); + BusApiUtils.requireMaxIds(routeIds, "route"); String routeIdsString = String.join(",", routeIds); @@ -126,7 +126,7 @@ private List makeRequest(String url) { .toList(); } - ApiUtils.checkErrors(errors, BusApiConstants.VEHICLES_ENDPOINT); + BusApiUtils.checkErrors(errors, BusApiConstants.VEHICLES_ENDPOINT); return List.of(); } diff --git a/src/main/java/com/cta4j/train/arrival/query/MapArrivalsQuery.java b/src/main/java/com/cta4j/train/arrival/query/MapArrivalsQuery.java index cb94f905..01a0ce1b 100644 --- a/src/main/java/com/cta4j/train/arrival/query/MapArrivalsQuery.java +++ b/src/main/java/com/cta4j/train/arrival/query/MapArrivalsQuery.java @@ -51,11 +51,7 @@ public static final class Builder { @Nullable private Integer maxResults; - /// Constructs a `Builder`. - /// - /// @param mapId the ID of the map to retrieve arrivals for - /// @throws NullPointerException if `mapId` is `null` - public Builder(String mapId) { + private Builder(String mapId) { this.mapId = Objects.requireNonNull(mapId); } diff --git a/src/main/java/com/cta4j/train/arrival/query/StopArrivalsQuery.java b/src/main/java/com/cta4j/train/arrival/query/StopArrivalsQuery.java index d2400f2d..46abdf8a 100644 --- a/src/main/java/com/cta4j/train/arrival/query/StopArrivalsQuery.java +++ b/src/main/java/com/cta4j/train/arrival/query/StopArrivalsQuery.java @@ -51,11 +51,7 @@ public static final class Builder { @Nullable private Integer maxResults; - /// Constructs a `Builder`. - /// - /// @param stopId the ID of the stop to retrieve arrivals for - /// @throws NullPointerException if `stopId` is `null` - public Builder(String stopId) { + private Builder(String stopId) { this.stopId = Objects.requireNonNull(stopId); } diff --git a/src/test/java/com/cta4j/bus/common/internal/util/ApiUtilsTest.java b/src/test/java/com/cta4j/bus/common/internal/util/BusApiUtilsTest.java similarity index 67% rename from src/test/java/com/cta4j/bus/common/internal/util/ApiUtilsTest.java rename to src/test/java/com/cta4j/bus/common/internal/util/BusApiUtilsTest.java index d9d47513..92b314d7 100644 --- a/src/test/java/com/cta4j/bus/common/internal/util/ApiUtilsTest.java +++ b/src/test/java/com/cta4j/bus/common/internal/util/BusApiUtilsTest.java @@ -8,7 +8,7 @@ import static org.assertj.core.api.Assertions.*; -class ApiUtilsTest { +class BusApiUtilsTest { private record TestError(String msg, boolean notFound) implements CtaError { } @@ -16,49 +16,49 @@ private record TestError(String msg, boolean notFound) implements CtaError { void requireMaxIds_doesNotThrow_whenIdsIsAtMax() { List ids = List.of("1", "2", "3", "4", "5", "6", "7", "8", "9", "10"); - assertThatCode(() -> ApiUtils.requireMaxIds(ids, "stop")).doesNotThrowAnyException(); + assertThatCode(() -> BusApiUtils.requireMaxIds(ids, "stop")).doesNotThrowAnyException(); } @Test void requireMaxIds_throwsIllegalArgumentException_whenIdsExceedsMax() { List ids = List.of("1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"); - assertThatIllegalArgumentException().isThrownBy(() -> ApiUtils.requireMaxIds(ids, "stop")) + assertThatIllegalArgumentException().isThrownBy(() -> BusApiUtils.requireMaxIds(ids, "stop")) .withMessage("A maximum of 10 stop IDs can be requested at once, but 11 were provided"); } @Test void requireMaxIds_throwsNullPointerException_whenIdsIsNull() { - assertThatNullPointerException().isThrownBy(() -> ApiUtils.requireMaxIds(null, "stop")); + assertThatNullPointerException().isThrownBy(() -> BusApiUtils.requireMaxIds(null, "stop")); } @Test void requireMaxIds_throwsNullPointerException_whenLabelIsNull() { - assertThatNullPointerException().isThrownBy(() -> ApiUtils.requireMaxIds(List.of(), null)); + assertThatNullPointerException().isThrownBy(() -> BusApiUtils.requireMaxIds(List.of(), null)); } @Test void checkErrors_doesNotThrow_whenErrorsIsNull() { - assertThatCode(() -> ApiUtils.checkErrors(null, "/test")).doesNotThrowAnyException(); + assertThatCode(() -> BusApiUtils.checkErrors(null, "/test")).doesNotThrowAnyException(); } @Test void checkErrors_doesNotThrow_whenErrorsIsEmpty() { - assertThatCode(() -> ApiUtils.checkErrors(List.of(), "/test")).doesNotThrowAnyException(); + assertThatCode(() -> BusApiUtils.checkErrors(List.of(), "/test")).doesNotThrowAnyException(); } @Test void checkErrors_doesNotThrow_whenAllErrorsAreNotFound() { List errors = List.of(new TestError("not found", true)); - assertThatCode(() -> ApiUtils.checkErrors(errors, "/test")).doesNotThrowAnyException(); + assertThatCode(() -> BusApiUtils.checkErrors(errors, "/test")).doesNotThrowAnyException(); } @Test void checkErrors_throwsCta4jBusException_whenAnyErrorIsNotResourceSpecific() { List errors = List.of(new TestError("fatal error", false)); - assertThatThrownBy(() -> ApiUtils.checkErrors(errors, "/test")) + assertThatThrownBy(() -> BusApiUtils.checkErrors(errors, "/test")) .isInstanceOf(Cta4jBusException.class) .hasMessage("fatal error") .satisfies(e -> assertThat(((Cta4jBusException) e).getEndpoint()).isEqualTo("/test")); @@ -66,6 +66,6 @@ void checkErrors_throwsCta4jBusException_whenAnyErrorIsNotResourceSpecific() { @Test void checkErrors_throwsNullPointerException_whenEndpointIsNull() { - assertThatNullPointerException().isThrownBy(() -> ApiUtils.checkErrors(List.of(), null)); + assertThatNullPointerException().isThrownBy(() -> BusApiUtils.checkErrors(List.of(), null)); } } From c337a6309caf250747cb4dd0b0dab745c4c5689b Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Fri, 31 Jul 2026 16:07:00 -0500 Subject: [PATCH 45/60] Update Javadoc comments to replace `@apiNote` with **NOTE:** for clarity --- CLAUDE.md | 2 +- src/main/java/com/cta4j/bus/detour/model/Detour.java | 4 ++-- .../com/cta4j/bus/prediction/model/PredictionMetadata.java | 4 ++-- src/main/java/com/cta4j/bus/route/model/Route.java | 4 ++-- src/main/java/com/cta4j/bus/stop/model/Stop.java | 4 ++-- .../java/com/cta4j/bus/vehicle/model/VehicleMetadata.java | 6 +++--- .../java/com/cta4j/train/common/model/ArrivalMetadata.java | 2 +- .../java/com/cta4j/train/common/model/TrainDirection.java | 2 +- .../java/com/cta4j/train/location/model/LocationTrain.java | 2 +- src/main/java/com/cta4j/train/station/StationsApi.java | 2 +- 10 files changed, 16 insertions(+), 16 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 7fa7beaa..3b318dbf 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -129,7 +129,7 @@ References: The static `builder(...)` method: "Creates a new `Builder` for constructing a/an `X`.", `@return` tag "a new `Builder`". The `build()` method: "Builds a configured `X` instance.", `@return` tag "a new `X`". -- **Tag order:** `@apiNote` → `@param` → `@return` → `@deprecated` → `@since` → `@throws` +- **Tag order:** `@param` → `@return` → `@deprecated` → `@since` → `@throws` → `@see`. - **@param / @throws descriptions:** Lowercase phrase, no trailing period. - **Code references:** Use backtick spans (`` `RoutesApi` ``, `` `List` ``) diff --git a/src/main/java/com/cta4j/bus/detour/model/Detour.java b/src/main/java/com/cta4j/bus/detour/model/Detour.java index 8a52f7e4..680ce0bb 100644 --- a/src/main/java/com/cta4j/bus/detour/model/Detour.java +++ b/src/main/java/com/cta4j/bus/detour/model/Detour.java @@ -9,8 +9,8 @@ /// Represents a service detour affecting one or more routes and directions within a specific time window. /// -/// @apiNote `dataFeed` is not well-documented by the CTA. As such, its presence here is primarily for completeness and -/// may not be populated or described correctly. +/// **NOTE:** `dataFeed` is not well-documented by the CTA. As such, its presence here is primarily for completeness +/// and may not be populated or described correctly. /// /// @param id the unique identifier of this detour /// @param version the version of this detour diff --git a/src/main/java/com/cta4j/bus/prediction/model/PredictionMetadata.java b/src/main/java/com/cta4j/bus/prediction/model/PredictionMetadata.java index 4c05129b..37c21286 100644 --- a/src/main/java/com/cta4j/bus/prediction/model/PredictionMetadata.java +++ b/src/main/java/com/cta4j/bus/prediction/model/PredictionMetadata.java @@ -9,8 +9,8 @@ /// Represents metadata associated with a bus arrival prediction. /// -/// @apiNote `gtfsSequence` and `nextBus` are not well-documented by the CTA. As such, their presence here is primarily -/// for completeness and may not be populated or described correctly. +/// **NOTE:** `gtfsSequence` and `nextBus` are not well-documented by the CTA. As such, their presence here is +/// primarily for completeness and may not be populated or described correctly. /// /// @param timestamp the date and time (UTC) this prediction was generated /// @param dynamicAction the [DynamicAction] affecting this prediction diff --git a/src/main/java/com/cta4j/bus/route/model/Route.java b/src/main/java/com/cta4j/bus/route/model/Route.java index 7e510cca..a571e3dc 100644 --- a/src/main/java/com/cta4j/bus/route/model/Route.java +++ b/src/main/java/com/cta4j/bus/route/model/Route.java @@ -7,8 +7,8 @@ /// Represents a bus route. /// -/// @apiNote `dataFeed` is not well-documented by the CTA. As such, its presence here is primarily for completeness and -/// may not be populated or described correctly. +/// **NOTE:** `dataFeed` is not well-documented by the CTA. As such, its presence here is primarily for completeness +/// and may not be populated or described correctly. /// /// @param id the alphanumeric designator of this route (e.g., "22", "J14", "X9") /// @param name the common name of this route (e.g., "Clark", "Jeffery Jump", "Ashland Express") diff --git a/src/main/java/com/cta4j/bus/stop/model/Stop.java b/src/main/java/com/cta4j/bus/stop/model/Stop.java index 1b791abd..5ef41df8 100644 --- a/src/main/java/com/cta4j/bus/stop/model/Stop.java +++ b/src/main/java/com/cta4j/bus/stop/model/Stop.java @@ -9,8 +9,8 @@ /// Represents a bus stop. /// -/// @apiNote `gtfsSequence` is not well-documented by the CTA. As such, its presence here is primarily for completeness -/// and may not be populated or described correctly. +/// **NOTE:** `gtfsSequence` is not well-documented by the CTA. As such, its presence here is primarily for +/// completeness and may not be populated or described correctly. /// /// @param id the unique identifier of this stop /// @param name the display name of this stop (e.g., "Clark & Addison") diff --git a/src/main/java/com/cta4j/bus/vehicle/model/VehicleMetadata.java b/src/main/java/com/cta4j/bus/vehicle/model/VehicleMetadata.java index 39408fea..82eb5ea0 100644 --- a/src/main/java/com/cta4j/bus/vehicle/model/VehicleMetadata.java +++ b/src/main/java/com/cta4j/bus/vehicle/model/VehicleMetadata.java @@ -10,9 +10,9 @@ /// Represents metadata associated with a vehicle. /// -/// @apiNote `dataFeed`, `stopStatus`, `timepointId`, `stopId`, `sequence`, `gtfsSequence`, `serverTimestamp`, `speed`, -/// and `block` are not well-documented by the CTA. As such, their presence here is primarily for completeness and may -/// not be populated or described correctly. +/// **NOTE:** `dataFeed`, `stopStatus`, `timepointId`, `stopId`, `sequence`, `gtfsSequence`, `serverTimestamp`, +/// `speed`, and `block` are not well-documented by the CTA. As such, their presence here is primarily for completeness +/// and may not be populated or described correctly. /// /// @param dataFeed the data feed from which this vehicle information was obtained, if applicable /// @param lastUpdated the date and time (UTC) this vehicle information was last updated, if applicable diff --git a/src/main/java/com/cta4j/train/common/model/ArrivalMetadata.java b/src/main/java/com/cta4j/train/common/model/ArrivalMetadata.java index 413f6d40..6f9c0920 100644 --- a/src/main/java/com/cta4j/train/common/model/ArrivalMetadata.java +++ b/src/main/java/com/cta4j/train/common/model/ArrivalMetadata.java @@ -8,7 +8,7 @@ /// Represents metadata associated with a train arrival. /// -/// @apiNote `flags` is not well-documented by the CTA. As such, its presence here is primarily for completeness and +/// **NOTE:** `flags` is not well-documented by the CTA. As such, its presence here is primarily for completeness and /// may not be populated or described correctly. /// /// @param runNumber the run number of the train associated with this arrival diff --git a/src/main/java/com/cta4j/train/common/model/TrainDirection.java b/src/main/java/com/cta4j/train/common/model/TrainDirection.java index f68a0ee7..b34707ba 100644 --- a/src/main/java/com/cta4j/train/common/model/TrainDirection.java +++ b/src/main/java/com/cta4j/train/common/model/TrainDirection.java @@ -4,7 +4,7 @@ /// Represents the operational direction of a train. /// -/// @apiNote This direction is operational in nature and does not necessarily reflect the physical direction of the +/// **NOTE:** This direction is operational in nature and does not necessarily reflect the physical direction of the /// train at its current location. It loosely translates to a northbound or southbound direction, though this may not /// be intuitive for all lines. @NullMarked diff --git a/src/main/java/com/cta4j/train/location/model/LocationTrain.java b/src/main/java/com/cta4j/train/location/model/LocationTrain.java index 9fc50163..79fec147 100644 --- a/src/main/java/com/cta4j/train/location/model/LocationTrain.java +++ b/src/main/java/com/cta4j/train/location/model/LocationTrain.java @@ -10,7 +10,7 @@ /// Represents the location of a train on a route. /// -/// @apiNote `flags` is not well-documented by the CTA. As such, its presence here is primarily for completeness and +/// **NOTE:** `flags` is not well-documented by the CTA. As such, its presence here is primarily for completeness and /// may not be populated or described correctly. /// /// @param run the run number of this train diff --git a/src/main/java/com/cta4j/train/station/StationsApi.java b/src/main/java/com/cta4j/train/station/StationsApi.java index eb460459..dc2bec39 100644 --- a/src/main/java/com/cta4j/train/station/StationsApi.java +++ b/src/main/java/com/cta4j/train/station/StationsApi.java @@ -10,7 +10,7 @@ /// /// This API allows retrieval of station information, including station names, IDs, and other details. /// -/// @apiNote The CTA Train Tracker API does not provide an endpoint for retrieving station information. This API uses +/// **NOTE:** The CTA Train Tracker API does not provide an endpoint for retrieving station information. This API uses /// the City of Chicago's Data Portal as its data source by default. The URL used to retrieve station information is /// configurable to accommodate changes to the data source. @NullMarked From b626d1da1ccf507faf3282fcbaf861eb380c2be3 Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Fri, 31 Jul 2026 17:29:51 -0500 Subject: [PATCH 46/60] Add unknown train line after seeing CTA behavior --- .../cta4j/train/common/model/TrainLine.java | 14 ++++++++++---- .../cta4j/train/common/ArrivalMapperTest.java | 19 +++++++++++++++++++ .../train/common/TrainQualifiersTest.java | 5 +++++ .../train/common/model/TrainLineTest.java | 11 +++++++++-- 4 files changed, 43 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/cta4j/train/common/model/TrainLine.java b/src/main/java/com/cta4j/train/common/model/TrainLine.java index 632484f0..b0537b42 100644 --- a/src/main/java/com/cta4j/train/common/model/TrainLine.java +++ b/src/main/java/com/cta4j/train/common/model/TrainLine.java @@ -29,7 +29,13 @@ public enum TrainLine { PINK("Pink", "#E27EA6"), /// Indicates the Yellow Line. - YELLOW("Y", "#F9E300"); + YELLOW("Y", "#F9E300"), + + /// Indicates that the train line is unknown. + /// + /// The CTA will occasionally report an unknown line for an arrival, even though "unknown" is not itself a valid + /// train line. + UNKNOWN("N/A", "#808080"); private final String code; private final String colorHex; @@ -56,8 +62,8 @@ public String getColorHex() { /// Returns the `TrainLine` corresponding to the given code. /// /// @param code the CTA code of the train line (case-insensitive, may include "LINE" suffix) - /// @return the corresponding `TrainLine` - /// @throws IllegalArgumentException if the code does not correspond to any known train line + /// @return the corresponding `TrainLine`, or `TrainLine.UNKNOWN` if the code does not correspond to any known + /// train line public static TrainLine fromCode(String code) { Objects.requireNonNull(code); @@ -70,7 +76,7 @@ public static TrainLine fromCode(String code) { case "P", "PURPLE LINE" -> TrainLine.PURPLE; case "PINK", "PINK LINE" -> TrainLine.PINK; case "Y", "YELLOW LINE" -> TrainLine.YELLOW; - default -> throw new IllegalArgumentException("Invalid train line: %s".formatted(code)); + default -> TrainLine.UNKNOWN; }; } } diff --git a/src/test/java/com/cta4j/train/common/ArrivalMapperTest.java b/src/test/java/com/cta4j/train/common/ArrivalMapperTest.java index 3d0f2fad..8c30b690 100644 --- a/src/test/java/com/cta4j/train/common/ArrivalMapperTest.java +++ b/src/test/java/com/cta4j/train/common/ArrivalMapperTest.java @@ -75,4 +75,23 @@ void toDomain_mapsApproachingTrue() { assertThat(arrival.approaching()).isTrue(); } + + @Test + void toDomain_mapsUnknownLine_whenRtIsNotApplicable() { + CtaArrival wire = new CtaArrival( + "40100", "30070", + "Howard", "Service toward O'Hare", + "123", "N/A", + "30077", "O'Hare", + "1", + "2015-04-30T20:23:53", + "2015-04-30T20:25:00", + "0", "0", "0", "0", + null, null, null, null + ); + + Arrival arrival = ArrivalMapper.INSTANCE.toDomain(wire); + + assertThat(arrival.line()).isEqualTo(TrainLine.UNKNOWN); + } } diff --git a/src/test/java/com/cta4j/train/common/TrainQualifiersTest.java b/src/test/java/com/cta4j/train/common/TrainQualifiersTest.java index 4e134545..b9e7fec5 100644 --- a/src/test/java/com/cta4j/train/common/TrainQualifiersTest.java +++ b/src/test/java/com/cta4j/train/common/TrainQualifiersTest.java @@ -135,6 +135,11 @@ void mapLine_returnsRed_whenLineIsRed() { assertThat(Qualifiers.mapLine("RED")).isEqualTo(TrainLine.RED); } + @Test + void mapLine_returnsUnknown_whenLineIsNotApplicable() { + assertThat(Qualifiers.mapLine("N/A")).isEqualTo(TrainLine.UNKNOWN); + } + @Test void mapTimestamp_returnsInstant_whenTimestampIsValid() { Instant instant = Qualifiers.mapTimestamp("2015-04-30T20:23:53"); diff --git a/src/test/java/com/cta4j/train/common/model/TrainLineTest.java b/src/test/java/com/cta4j/train/common/model/TrainLineTest.java index 4ffd8f29..6d5f64a8 100644 --- a/src/test/java/com/cta4j/train/common/model/TrainLineTest.java +++ b/src/test/java/com/cta4j/train/common/model/TrainLineTest.java @@ -26,8 +26,9 @@ void fromCode_returnsCorrectValues() { } @Test - void fromCode_throwsIllegalArgumentException_whenCodeIsUnknown() { - assertThatIllegalArgumentException().isThrownBy(() -> TrainLine.fromCode("Unknown")); + void fromCode_returnsUnknown_whenCodeIsUnrecognized() { + assertThat(TrainLine.fromCode("N/A")).isEqualTo(TrainLine.UNKNOWN); + assertThat(TrainLine.fromCode("Unknown")).isEqualTo(TrainLine.UNKNOWN); } @Test @@ -35,4 +36,10 @@ void getCode_andGetColorHex_returnValues() { assertThat(TrainLine.RED.getCode()).isEqualTo("Red"); assertThat(TrainLine.RED.getColorHex()).isEqualTo("#C60C30"); } + + @Test + void getCode_andGetColorHex_returnValues_forUnknown() { + assertThat(TrainLine.UNKNOWN.getCode()).isEqualTo("N/A"); + assertThat(TrainLine.UNKNOWN.getColorHex()).isEqualTo("#808080"); + } } From 6b2036e23e10fef27d1f34311f9cd608d721f0be Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Fri, 31 Jul 2026 20:19:53 -0500 Subject: [PATCH 47/60] Add support for multiple map and stop IDs for the arrivals api --- .../com/cta4j/train/arrival/ArrivalsApi.java | 72 +++++-- .../internal/impl/ArrivalsApiImpl.java | 24 ++- .../train/arrival/query/MapArrivalsQuery.java | 39 ++-- .../arrival/query/StopArrivalsQuery.java | 39 ++-- .../common/internal/util/TrainApiUtils.java | 32 ++++ .../train/arrival/ArrivalsApiImplTest.java | 178 +++++++++++++----- .../arrival/query/MapArrivalsQueryTest.java | 49 ++++- .../arrival/query/StopArrivalsQueryTest.java | 49 ++++- .../internal/util/TrainApiUtilsTest.java | 34 ++++ 9 files changed, 406 insertions(+), 110 deletions(-) create mode 100644 src/main/java/com/cta4j/train/common/internal/util/TrainApiUtils.java create mode 100644 src/test/java/com/cta4j/train/common/internal/util/TrainApiUtilsTest.java diff --git a/src/main/java/com/cta4j/train/arrival/ArrivalsApi.java b/src/main/java/com/cta4j/train/arrival/ArrivalsApi.java index c37dc33f..5acd1655 100644 --- a/src/main/java/com/cta4j/train/arrival/ArrivalsApi.java +++ b/src/main/java/com/cta4j/train/arrival/ArrivalsApi.java @@ -6,21 +6,41 @@ import com.cta4j.train.common.model.Arrival; import org.jspecify.annotations.NullMarked; +import java.util.Collection; import java.util.List; +import java.util.Objects; /// Provides access to arrival-related endpoints of the CTA Train Tracker API. /// -/// This API allows retrieval of arrivals by map ID or stop ID. +/// This API allows retrieval of arrivals by map IDs or stop IDs. @NullMarked public interface ArrivalsApi { - /// Retrieves arrivals by map ID. + /// Retrieves arrivals by map IDs. /// - /// @param query the query parameters for fetching arrivals by map ID - /// @return a [List] of [Arrival]s corresponding to the provided map ID, or an empty [List] if no arrivals are + /// @param query the query parameters for fetching arrivals by map IDs + /// @return a [List] of [Arrival]s corresponding to the provided map IDs, or an empty [List] if no arrivals are /// found /// @throws NullPointerException if `query` is `null` /// @throws Cta4jArrivalsException if the API returns an error response or the response cannot be parsed - List findByMapId(MapArrivalsQuery query); + List findByMapIds(MapArrivalsQuery query); + + /// Retrieves arrivals by map IDs. + /// + /// @param mapIds a [Collection] of map IDs + /// @return a [List] of [Arrival]s corresponding to the provided map IDs, or an empty [List] if no arrivals are + /// found + /// @throws NullPointerException if `mapIds` is `null`, or if any element of `mapIds` is `null` + /// @throws Cta4jArrivalsException if the API returns an error response or the response cannot be parsed + default List findByMapIds(Collection mapIds) { + Objects.requireNonNull(mapIds); + + List mapIdsList = List.copyOf(mapIds); + + MapArrivalsQuery query = MapArrivalsQuery.builder(mapIdsList) + .build(); + + return this.findByMapIds(query); + } /// Retrieves arrivals by map ID. /// @@ -30,20 +50,42 @@ public interface ArrivalsApi { /// @throws NullPointerException if `mapId` is `null` /// @throws Cta4jArrivalsException if the API returns an error response or the response cannot be parsed default List findByMapId(String mapId) { - MapArrivalsQuery query = MapArrivalsQuery.builder(mapId) + Objects.requireNonNull(mapId); + + List mapIds = List.of(mapId); + + MapArrivalsQuery query = MapArrivalsQuery.builder(mapIds) .build(); - return this.findByMapId(query); + return this.findByMapIds(query); } - /// Retrieves arrivals by stop ID. + /// Retrieves arrivals by stop IDs. /// - /// @param query the query parameters for fetching arrivals by stop ID - /// @return a [List] of [Arrival]s corresponding to the provided stop ID, or an empty [List] if no arrivals are + /// @param query the query parameters for fetching arrivals by stop IDs + /// @return a [List] of [Arrival]s corresponding to the provided stop IDs, or an empty [List] if no arrivals are /// found /// @throws NullPointerException if `query` is `null` /// @throws Cta4jArrivalsException if the API returns an error response or the response cannot be parsed - List findByStopId(StopArrivalsQuery query); + List findByStopIds(StopArrivalsQuery query); + + /// Retrieves arrivals by stop IDs. + /// + /// @param stopIds a [Collection] of stop IDs + /// @return a [List] of [Arrival]s corresponding to the provided stop IDs, or an empty [List] if no arrivals are + /// found + /// @throws NullPointerException if `stopIds` is `null`, or if any element of `stopIds` is `null` + /// @throws Cta4jArrivalsException if the API returns an error response or the response cannot be parsed + default List findByStopIds(Collection stopIds) { + Objects.requireNonNull(stopIds); + + List stopIdsList = List.copyOf(stopIds); + + StopArrivalsQuery query = StopArrivalsQuery.builder(stopIdsList) + .build(); + + return this.findByStopIds(query); + } /// Retrieves arrivals by stop ID. /// @@ -53,9 +95,13 @@ default List findByMapId(String mapId) { /// @throws NullPointerException if `stopId` is `null` /// @throws Cta4jArrivalsException if the API returns an error response or the response cannot be parsed default List findByStopId(String stopId) { - StopArrivalsQuery query = StopArrivalsQuery.builder(stopId) + Objects.requireNonNull(stopId); + + List stopIds = List.of(stopId); + + StopArrivalsQuery query = StopArrivalsQuery.builder(stopIds) .build(); - return this.findByStopId(query); + return this.findByStopIds(query); } } diff --git a/src/main/java/com/cta4j/train/arrival/internal/impl/ArrivalsApiImpl.java b/src/main/java/com/cta4j/train/arrival/internal/impl/ArrivalsApiImpl.java index c3f8566c..abed5fdd 100644 --- a/src/main/java/com/cta4j/train/arrival/internal/impl/ArrivalsApiImpl.java +++ b/src/main/java/com/cta4j/train/arrival/internal/impl/ArrivalsApiImpl.java @@ -38,15 +38,23 @@ public ArrivalsApiImpl(TrainApiConfig config) { } @Override - public List findByMapId(MapArrivalsQuery query) { + public List findByMapIds(MapArrivalsQuery query) { Objects.requireNonNull(query); + List mapIds = query.mapIds(); + + if (mapIds.isEmpty()) { + return List.of(); + } + + String mapIdsString = String.join(",", mapIds); + URIBuilder builder = new URIBuilder() .setScheme(this.config.scheme()) .setHost(this.config.host()) .setPort(this.config.port()) .setPath(TrainApiConstants.ARRIVALS_ENDPOINT) - .addParameter("mapid", query.mapId()) + .addParameter("mapid", mapIdsString) .addParameter("key", this.config.apiKey()) .addParameter("outputType", "JSON"); @@ -54,15 +62,23 @@ public List findByMapId(MapArrivalsQuery query) { } @Override - public List findByStopId(StopArrivalsQuery query) { + public List findByStopIds(StopArrivalsQuery query) { Objects.requireNonNull(query); + List stopIds = query.stopIds(); + + if (stopIds.isEmpty()) { + return List.of(); + } + + String stopIdsString = String.join(",", stopIds); + URIBuilder builder = new URIBuilder() .setScheme(this.config.scheme()) .setHost(this.config.host()) .setPort(this.config.port()) .setPath(TrainApiConstants.ARRIVALS_ENDPOINT) - .addParameter("stpid", query.stopId()) + .addParameter("stpid", stopIdsString) .addParameter("key", this.config.apiKey()) .addParameter("outputType", "JSON"); diff --git a/src/main/java/com/cta4j/train/arrival/query/MapArrivalsQuery.java b/src/main/java/com/cta4j/train/arrival/query/MapArrivalsQuery.java index 01a0ce1b..7183c884 100644 --- a/src/main/java/com/cta4j/train/arrival/query/MapArrivalsQuery.java +++ b/src/main/java/com/cta4j/train/arrival/query/MapArrivalsQuery.java @@ -1,31 +1,39 @@ package com.cta4j.train.arrival.query; +import com.cta4j.train.common.internal.util.TrainApiUtils; import com.cta4j.train.common.model.TrainLine; import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; +import java.util.Collection; +import java.util.List; import java.util.Objects; /// Represents a query for train arrivals at a specific map. /// -/// @param mapId the ID of the map to retrieve arrivals for +/// @param mapIds the [List] of map IDs to retrieve arrivals for /// @param line the optional train line to filter arrivals by /// @param maxResults the optional maximum number of arrivals to return @NullMarked public record MapArrivalsQuery( - String mapId, + List mapIds, @Nullable TrainLine line, @Nullable Integer maxResults ) { /// Constructs a `MapArrivalsQuery`. /// - /// @param mapId the ID of the map to retrieve arrivals for + /// @param mapIds the [List] of map IDs to retrieve arrivals for /// @param line the optional train line to filter arrivals by /// @param maxResults the optional maximum number of arrivals to return - /// @throws NullPointerException if `mapId` is `null` - /// @throws IllegalArgumentException if `maxResults` is non-`null` and not positive + /// @throws NullPointerException if `mapIds` is `null`, or if any element of `mapIds` is `null` + /// @throws IllegalArgumentException if more than 4 map IDs are provided, or if `maxResults` is non-`null` and not + /// positive public MapArrivalsQuery { - Objects.requireNonNull(mapId); + Objects.requireNonNull(mapIds); + + mapIds = List.copyOf(mapIds); + + TrainApiUtils.requireMaxIds(mapIds, "map"); if ((maxResults != null) && (maxResults <= 0)) { throw new IllegalArgumentException("maxResults must be positive"); @@ -34,16 +42,16 @@ public record MapArrivalsQuery( /// Creates a new `Builder` for constructing a `MapArrivalsQuery`. /// - /// @param mapId the ID of the map to retrieve arrivals for + /// @param mapIds the [Collection] of map IDs to retrieve arrivals for /// @return a new `Builder` - /// @throws NullPointerException if `mapId` is `null` - public static Builder builder(String mapId) { - return new Builder(mapId); + /// @throws NullPointerException if `mapIds` is `null`, or if any element of `mapIds` is `null` + public static Builder builder(Collection mapIds) { + return new Builder(mapIds); } /// A builder for `MapArrivalsQuery`. public static final class Builder { - private final String mapId; + private final List mapIds; @Nullable private TrainLine line; @@ -51,8 +59,10 @@ public static final class Builder { @Nullable private Integer maxResults; - private Builder(String mapId) { - this.mapId = Objects.requireNonNull(mapId); + private Builder(Collection mapIds) { + Objects.requireNonNull(mapIds); + + this.mapIds = List.copyOf(mapIds); } /// Sets the train line to filter arrivals by. @@ -84,9 +94,10 @@ public Builder maxResults(int maxResults) { /// Builds a configured `MapArrivalsQuery` instance. /// /// @return a new `MapArrivalsQuery` + /// @throws IllegalArgumentException if more than 4 map IDs are provided public MapArrivalsQuery build() { return new MapArrivalsQuery( - this.mapId, + this.mapIds, this.line, this.maxResults ); diff --git a/src/main/java/com/cta4j/train/arrival/query/StopArrivalsQuery.java b/src/main/java/com/cta4j/train/arrival/query/StopArrivalsQuery.java index 46abdf8a..5054b8a7 100644 --- a/src/main/java/com/cta4j/train/arrival/query/StopArrivalsQuery.java +++ b/src/main/java/com/cta4j/train/arrival/query/StopArrivalsQuery.java @@ -1,31 +1,39 @@ package com.cta4j.train.arrival.query; +import com.cta4j.train.common.internal.util.TrainApiUtils; import com.cta4j.train.common.model.TrainLine; import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; +import java.util.Collection; +import java.util.List; import java.util.Objects; /// Represents a query for train arrivals at a specific stop. /// -/// @param stopId the ID of the stop to retrieve arrivals for +/// @param stopIds the [List] of stop IDs to retrieve arrivals for /// @param line the optional train line to filter arrivals by /// @param maxResults the optional maximum number of arrivals to return @NullMarked public record StopArrivalsQuery( - String stopId, + List stopIds, @Nullable TrainLine line, @Nullable Integer maxResults ) { /// Constructs a `StopArrivalsQuery`. /// - /// @param stopId the ID of the stop to retrieve arrivals for + /// @param stopIds the [List] of stop IDs to retrieve arrivals for /// @param line the optional train line to filter arrivals by /// @param maxResults the optional maximum number of arrivals to return - /// @throws NullPointerException if `stopId` is `null` - /// @throws IllegalArgumentException if `maxResults` is non-`null` and not positive + /// @throws NullPointerException if `stopIds` is `null`, or if any element of `stopIds` is `null` + /// @throws IllegalArgumentException if more than 4 stop IDs are provided, or if `maxResults` is non-`null` and not + /// positive public StopArrivalsQuery { - Objects.requireNonNull(stopId); + Objects.requireNonNull(stopIds); + + stopIds = List.copyOf(stopIds); + + TrainApiUtils.requireMaxIds(stopIds, "stop"); if ((maxResults != null) && (maxResults <= 0)) { throw new IllegalArgumentException("maxResults must be positive"); @@ -34,16 +42,16 @@ public record StopArrivalsQuery( /// Creates a new `Builder` for constructing a `StopArrivalsQuery`. /// - /// @param stopId the ID of the stop to retrieve arrivals for + /// @param stopIds the [Collection] of stop IDs to retrieve arrivals for /// @return a new `Builder` - /// @throws NullPointerException if `stopId` is `null` - public static Builder builder(String stopId) { - return new Builder(stopId); + /// @throws NullPointerException if `stopIds` is `null`, or if any element of `stopIds` is `null` + public static Builder builder(Collection stopIds) { + return new Builder(stopIds); } /// A builder for `StopArrivalsQuery`. public static final class Builder { - private final String stopId; + private final List stopIds; @Nullable private TrainLine line; @@ -51,8 +59,10 @@ public static final class Builder { @Nullable private Integer maxResults; - private Builder(String stopId) { - this.stopId = Objects.requireNonNull(stopId); + private Builder(Collection stopIds) { + Objects.requireNonNull(stopIds); + + this.stopIds = List.copyOf(stopIds); } /// Sets the train line to filter arrivals by. @@ -84,9 +94,10 @@ public Builder maxResults(int maxResults) { /// Builds a configured `StopArrivalsQuery` instance. /// /// @return a new `StopArrivalsQuery` + /// @throws IllegalArgumentException if more than 4 stop IDs are provided public StopArrivalsQuery build() { return new StopArrivalsQuery( - this.stopId, + this.stopIds, this.line, this.maxResults ); diff --git a/src/main/java/com/cta4j/train/common/internal/util/TrainApiUtils.java b/src/main/java/com/cta4j/train/common/internal/util/TrainApiUtils.java new file mode 100644 index 00000000..0e51779a --- /dev/null +++ b/src/main/java/com/cta4j/train/common/internal/util/TrainApiUtils.java @@ -0,0 +1,32 @@ +package com.cta4j.train.common.internal.util; + +import org.jetbrains.annotations.ApiStatus; +import org.jspecify.annotations.NullMarked; + +import java.util.Collection; +import java.util.Objects; + +@ApiStatus.Internal +@NullMarked +public final class TrainApiUtils { + public static final int MAX_IDS_PER_REQUEST = 4; + + private TrainApiUtils() { + throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); + } + + public static void requireMaxIds(Collection ids, String label) { + Objects.requireNonNull(ids); + Objects.requireNonNull(label); + + if (ids.size() > MAX_IDS_PER_REQUEST) { + String message = "A maximum of %d %s IDs can be requested at once, but %d were provided".formatted( + MAX_IDS_PER_REQUEST, + label, + ids.size() + ); + + throw new IllegalArgumentException(message); + } + } +} diff --git a/src/test/java/com/cta4j/train/arrival/ArrivalsApiImplTest.java b/src/test/java/com/cta4j/train/arrival/ArrivalsApiImplTest.java index 6cb6b5e9..e4a6dc5e 100644 --- a/src/test/java/com/cta4j/train/arrival/ArrivalsApiImplTest.java +++ b/src/test/java/com/cta4j/train/arrival/ArrivalsApiImplTest.java @@ -45,15 +45,15 @@ void tearDown() { } @Test - void findByMapId_returnsArrivals_whenResponseContainsArrivals() { + void findByMapIds_returnsArrivals_whenResponseContainsArrivals() { this.server.stubFor(get(urlPathEqualTo("/api/1.0/ttarrivals.aspx")) .willReturn(aResponse() .withStatus(200) .withHeader("Content-Type", "application/json") .withBody(TestFixtures.read("train/arrival/success.json")))); - MapArrivalsQuery query = MapArrivalsQuery.builder("40900").build(); - List arrivals = this.api.findByMapId(query); + MapArrivalsQuery query = MapArrivalsQuery.builder(List.of("40900")).build(); + List arrivals = this.api.findByMapIds(query); assertThat(arrivals).hasSize(1); Arrival arrival = arrivals.getFirst(); @@ -65,44 +65,68 @@ void findByMapId_returnsArrivals_whenResponseContainsArrivals() { } @Test - void findByMapId_returnsEmpty_whenResponseHasNoEta() { + void findByMapIds_sendsCommaDelimitedMapIds_whenMultipleProvided() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/ttarrivals.aspx")) + .withQueryParam("mapid", equalTo("40900,40380,40360")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("train/arrival/success.json")))); + + MapArrivalsQuery query = MapArrivalsQuery.builder(List.of("40900", "40380", "40360")).build(); + List arrivals = this.api.findByMapIds(query); + + assertThat(arrivals).hasSize(1); + } + + @Test + void findByMapIds_returnsEmpty_whenMapIdsIsEmpty() { + MapArrivalsQuery query = MapArrivalsQuery.builder(List.of()).build(); + List arrivals = this.api.findByMapIds(query); + + assertThat(arrivals).isEmpty(); + this.server.verify(0, getRequestedFor(urlPathEqualTo("/api/1.0/ttarrivals.aspx"))); + } + + @Test + void findByMapIds_returnsEmpty_whenResponseHasNoEta() { this.server.stubFor(get(urlPathEqualTo("/api/1.0/ttarrivals.aspx")) .willReturn(aResponse() .withStatus(200) .withHeader("Content-Type", "application/json") .withBody(TestFixtures.read("train/arrival/empty.json")))); - MapArrivalsQuery query = MapArrivalsQuery.builder("40900").build(); - List arrivals = this.api.findByMapId(query); + MapArrivalsQuery query = MapArrivalsQuery.builder(List.of("40900")).build(); + List arrivals = this.api.findByMapIds(query); assertThat(arrivals).isEmpty(); } @Test - void findByMapId_returnsEmpty_whenEtaIsEmptyArray() { + void findByMapIds_returnsEmpty_whenEtaIsEmptyArray() { this.server.stubFor(get(urlPathEqualTo("/api/1.0/ttarrivals.aspx")) .willReturn(aResponse() .withStatus(200) .withHeader("Content-Type", "application/json") .withBody("{\"ctatt\":{\"tmst\":\"2015-04-30T20:23:53\",\"errCd\":\"0\",\"errNm\":null,\"eta\":[]}}"))); - MapArrivalsQuery query = MapArrivalsQuery.builder("40900").build(); - List arrivals = this.api.findByMapId(query); + MapArrivalsQuery query = MapArrivalsQuery.builder(List.of("40900")).build(); + List arrivals = this.api.findByMapIds(query); assertThat(arrivals).isEmpty(); } @Test - void findByMapId_throwsCta4jArrivalsException_whenResponseContainsError() { + void findByMapIds_throwsCta4jArrivalsException_whenResponseContainsError() { this.server.stubFor(get(urlPathEqualTo("/api/1.0/ttarrivals.aspx")) .willReturn(aResponse() .withStatus(200) .withHeader("Content-Type", "application/json") .withBody(TestFixtures.read("train/arrival/error.json")))); - MapArrivalsQuery query = MapArrivalsQuery.builder("40900").build(); + MapArrivalsQuery query = MapArrivalsQuery.builder(List.of("40900")).build(); - assertThatThrownBy(() -> this.api.findByMapId(query)) + assertThatThrownBy(() -> this.api.findByMapIds(query)) .isInstanceOf(Cta4jArrivalsException.class) .hasMessage("Invalid API key") .satisfies(e -> assertThat(((Cta4jArrivalsException) e).getErrorCode()) @@ -111,30 +135,30 @@ void findByMapId_throwsCta4jArrivalsException_whenResponseContainsError() { } @Test - void findByMapId_returnsEmpty_whenResponseContainsInvalidMapIdError() { + void findByMapIds_returnsEmpty_whenResponseContainsInvalidMapIdError() { this.server.stubFor(get(urlPathEqualTo("/api/1.0/ttarrivals.aspx")) .willReturn(aResponse() .withStatus(200) .withHeader("Content-Type", "application/json") .withBody(TestFixtures.read("train/arrival/not_found_mapid.json")))); - MapArrivalsQuery query = MapArrivalsQuery.builder("99999").build(); - List arrivals = this.api.findByMapId(query); + MapArrivalsQuery query = MapArrivalsQuery.builder(List.of("99999")).build(); + List arrivals = this.api.findByMapIds(query); assertThat(arrivals).isEmpty(); } @Test - void findByMapId_throwsCta4jArrivalsException_whenResponseIsNotJson() { + void findByMapIds_throwsCta4jArrivalsException_whenResponseIsNotJson() { this.server.stubFor(get(urlPathEqualTo("/api/1.0/ttarrivals.aspx")) .willReturn(aResponse() .withStatus(200) .withHeader("Content-Type", "application/json") .withBody("not-json"))); - MapArrivalsQuery query = MapArrivalsQuery.builder("40900").build(); + MapArrivalsQuery query = MapArrivalsQuery.builder(List.of("40900")).build(); - assertThatThrownBy(() -> this.api.findByMapId(query)) + assertThatThrownBy(() -> this.api.findByMapIds(query)) .isInstanceOf(Cta4jArrivalsException.class) .hasMessage("Failed to parse response") .satisfies(e -> assertThat(((Cta4jArrivalsException) e).getErrorCode()).isNull()) @@ -142,7 +166,7 @@ void findByMapId_throwsCta4jArrivalsException_whenResponseIsNotJson() { } @Test - void findByStopId_returnsArrivals_whenResponseContainsArrivals() { + void findByStopIds_returnsArrivals_whenResponseContainsArrivals() { this.server.stubFor(get(urlPathEqualTo("/api/1.0/ttarrivals.aspx")) .withQueryParam("stpid", equalTo("30070")) .willReturn(aResponse() @@ -150,37 +174,61 @@ void findByStopId_returnsArrivals_whenResponseContainsArrivals() { .withHeader("Content-Type", "application/json") .withBody(TestFixtures.read("train/arrival/success.json")))); - StopArrivalsQuery query = StopArrivalsQuery.builder("30070").build(); - List arrivals = this.api.findByStopId(query); + StopArrivalsQuery query = StopArrivalsQuery.builder(List.of("30070")).build(); + List arrivals = this.api.findByStopIds(query); + + assertThat(arrivals).hasSize(1); + } + + @Test + void findByStopIds_sendsCommaDelimitedStopIds_whenMultipleProvided() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/ttarrivals.aspx")) + .withQueryParam("stpid", equalTo("30070,30071,30375")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("train/arrival/success.json")))); + + StopArrivalsQuery query = StopArrivalsQuery.builder(List.of("30070", "30071", "30375")).build(); + List arrivals = this.api.findByStopIds(query); assertThat(arrivals).hasSize(1); } @Test - void findByStopId_returnsEmpty_whenResponseHasNoEta() { + void findByStopIds_returnsEmpty_whenStopIdsIsEmpty() { + StopArrivalsQuery query = StopArrivalsQuery.builder(List.of()).build(); + List arrivals = this.api.findByStopIds(query); + + assertThat(arrivals).isEmpty(); + this.server.verify(0, getRequestedFor(urlPathEqualTo("/api/1.0/ttarrivals.aspx"))); + } + + @Test + void findByStopIds_returnsEmpty_whenResponseHasNoEta() { this.server.stubFor(get(urlPathEqualTo("/api/1.0/ttarrivals.aspx")) .willReturn(aResponse() .withStatus(200) .withHeader("Content-Type", "application/json") .withBody(TestFixtures.read("train/arrival/empty.json")))); - StopArrivalsQuery query = StopArrivalsQuery.builder("30070").build(); - List arrivals = this.api.findByStopId(query); + StopArrivalsQuery query = StopArrivalsQuery.builder(List.of("30070")).build(); + List arrivals = this.api.findByStopIds(query); assertThat(arrivals).isEmpty(); } @Test - void findByStopId_throwsCta4jArrivalsException_whenResponseContainsError() { + void findByStopIds_throwsCta4jArrivalsException_whenResponseContainsError() { this.server.stubFor(get(urlPathEqualTo("/api/1.0/ttarrivals.aspx")) .willReturn(aResponse() .withStatus(200) .withHeader("Content-Type", "application/json") .withBody(TestFixtures.read("train/arrival/error.json")))); - StopArrivalsQuery query = StopArrivalsQuery.builder("30070").build(); + StopArrivalsQuery query = StopArrivalsQuery.builder(List.of("30070")).build(); - assertThatThrownBy(() -> this.api.findByStopId(query)) + assertThatThrownBy(() -> this.api.findByStopIds(query)) .isInstanceOf(Cta4jArrivalsException.class) .hasMessage("Invalid API key") .satisfies(e -> assertThat(((Cta4jArrivalsException) e).getErrorCode()) @@ -189,21 +237,21 @@ void findByStopId_throwsCta4jArrivalsException_whenResponseContainsError() { } @Test - void findByStopId_returnsEmpty_whenResponseContainsInvalidStopIdError() { + void findByStopIds_returnsEmpty_whenResponseContainsInvalidStopIdError() { this.server.stubFor(get(urlPathEqualTo("/api/1.0/ttarrivals.aspx")) .willReturn(aResponse() .withStatus(200) .withHeader("Content-Type", "application/json") .withBody(TestFixtures.read("train/arrival/not_found_stpid.json")))); - StopArrivalsQuery query = StopArrivalsQuery.builder("99999").build(); - List arrivals = this.api.findByStopId(query); + StopArrivalsQuery query = StopArrivalsQuery.builder(List.of("99999")).build(); + List arrivals = this.api.findByStopIds(query); assertThat(arrivals).isEmpty(); } @Test - void findByMapId_sendsLineAndMaxResultsQueryParams_whenSet() { + void findByMapIds_sendsLineAndMaxResultsQueryParams_whenSet() { this.server.stubFor(get(urlPathEqualTo("/api/1.0/ttarrivals.aspx")) .withQueryParam("rt", equalTo("Red")) .withQueryParam("max", equalTo("5")) @@ -212,18 +260,18 @@ void findByMapId_sendsLineAndMaxResultsQueryParams_whenSet() { .withHeader("Content-Type", "application/json") .withBody(TestFixtures.read("train/arrival/success.json")))); - MapArrivalsQuery query = MapArrivalsQuery.builder("40900") + MapArrivalsQuery query = MapArrivalsQuery.builder(List.of("40900")) .line(TrainLine.RED) .maxResults(5) .build(); - List arrivals = this.api.findByMapId(query); + List arrivals = this.api.findByMapIds(query); assertThat(arrivals).hasSize(1); } @Test - void findByStopId_sendsLineAndMaxResultsQueryParams_whenSet() { + void findByStopIds_sendsLineAndMaxResultsQueryParams_whenSet() { this.server.stubFor(get(urlPathEqualTo("/api/1.0/ttarrivals.aspx")) .withQueryParam("stpid", equalTo("30070")) .withQueryParam("rt", equalTo("Red")) @@ -233,12 +281,40 @@ void findByStopId_sendsLineAndMaxResultsQueryParams_whenSet() { .withHeader("Content-Type", "application/json") .withBody(TestFixtures.read("train/arrival/success.json")))); - StopArrivalsQuery query = StopArrivalsQuery.builder("30070") + StopArrivalsQuery query = StopArrivalsQuery.builder(List.of("30070")) .line(TrainLine.RED) .maxResults(5) .build(); - List arrivals = this.api.findByStopId(query); + List arrivals = this.api.findByStopIds(query); + + assertThat(arrivals).hasSize(1); + } + + @Test + void findByMapIds_collectionOverload_returnsArrivals_whenResponseContainsArrivals() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/ttarrivals.aspx")) + .withQueryParam("mapid", equalTo("40900,40380")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("train/arrival/success.json")))); + + List arrivals = this.api.findByMapIds(List.of("40900", "40380")); + + assertThat(arrivals).hasSize(1); + } + + @Test + void findByStopIds_collectionOverload_returnsArrivals_whenResponseContainsArrivals() { + this.server.stubFor(get(urlPathEqualTo("/api/1.0/ttarrivals.aspx")) + .withQueryParam("stpid", equalTo("30070,30071")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("train/arrival/success.json")))); + + List arrivals = this.api.findByStopIds(List.of("30070", "30071")); assertThat(arrivals).hasSize(1); } @@ -271,16 +347,16 @@ void findByStopId_stringOverload_returnsArrivals_whenResponseContainsArrivals() } @Test - void findByMapId_throwsCta4jArrivalsException_whenErrCdIsNotNumeric() { + void findByMapIds_throwsCta4jArrivalsException_whenErrCdIsNotNumeric() { this.server.stubFor(get(urlPathEqualTo("/api/1.0/ttarrivals.aspx")) .willReturn(aResponse() .withStatus(200) .withHeader("Content-Type", "application/json") .withBody(TestFixtures.read("train/arrival/invalid_err_cd.json")))); - MapArrivalsQuery query = MapArrivalsQuery.builder("40900").build(); + MapArrivalsQuery query = MapArrivalsQuery.builder(List.of("40900")).build(); - assertThatThrownBy(() -> this.api.findByMapId(query)) + assertThatThrownBy(() -> this.api.findByMapIds(query)) .isInstanceOf(Cta4jArrivalsException.class) .hasMessage("Failed to parse error code") .satisfies(e -> assertThat(((Cta4jArrivalsException) e).getErrorCode()).isNull()) @@ -288,16 +364,16 @@ void findByMapId_throwsCta4jArrivalsException_whenErrCdIsNotNumeric() { } @Test - void findByMapId_throwsCta4jArrivalsException_whenErrCdIsNegative() { + void findByMapIds_throwsCta4jArrivalsException_whenErrCdIsNegative() { this.server.stubFor(get(urlPathEqualTo("/api/1.0/ttarrivals.aspx")) .willReturn(aResponse() .withStatus(200) .withHeader("Content-Type", "application/json") .withBody("{\"ctatt\":{\"tmst\":\"2015-04-30T20:23:53\",\"errCd\":\"-1\",\"errNm\":\"Unexpected error\"}}"))); - MapArrivalsQuery query = MapArrivalsQuery.builder("40900").build(); + MapArrivalsQuery query = MapArrivalsQuery.builder(List.of("40900")).build(); - assertThatThrownBy(() -> this.api.findByMapId(query)) + assertThatThrownBy(() -> this.api.findByMapIds(query)) .isInstanceOf(Cta4jArrivalsException.class) .hasMessage("Unknown error code") .satisfies(e -> assertThat(((Cta4jArrivalsException) e).getErrorCode()) @@ -306,16 +382,16 @@ void findByMapId_throwsCta4jArrivalsException_whenErrCdIsNegative() { } @Test - void findByMapId_throwsCta4jArrivalsException_withDefaultMessage_whenErrNmIsBlank() { + void findByMapIds_throwsCta4jArrivalsException_withDefaultMessage_whenErrNmIsBlank() { this.server.stubFor(get(urlPathEqualTo("/api/1.0/ttarrivals.aspx")) .willReturn(aResponse() .withStatus(200) .withHeader("Content-Type", "application/json") .withBody("{\"ctatt\":{\"tmst\":\"2015-04-30T20:23:53\",\"errCd\":\"1\",\"errNm\":\"\"}}"))); - MapArrivalsQuery query = MapArrivalsQuery.builder("40900").build(); + MapArrivalsQuery query = MapArrivalsQuery.builder(List.of("40900")).build(); - assertThatThrownBy(() -> this.api.findByMapId(query)) + assertThatThrownBy(() -> this.api.findByMapIds(query)) .isInstanceOf(Cta4jArrivalsException.class) .hasMessage("An unknown error occurred.") .satisfies(e -> assertThat(((Cta4jArrivalsException) e).getErrorCode()) @@ -324,16 +400,16 @@ void findByMapId_throwsCta4jArrivalsException_withDefaultMessage_whenErrNmIsBlan } @Test - void findByMapId_throwsCta4jArrivalsException_withDefaultMessage_whenErrNmIsAbsent() { + void findByMapIds_throwsCta4jArrivalsException_withDefaultMessage_whenErrNmIsAbsent() { this.server.stubFor(get(urlPathEqualTo("/api/1.0/ttarrivals.aspx")) .willReturn(aResponse() .withStatus(200) .withHeader("Content-Type", "application/json") .withBody("{\"ctatt\":{\"tmst\":\"2015-04-30T20:23:53\",\"errCd\":\"1\"}}"))); - MapArrivalsQuery query = MapArrivalsQuery.builder("40900").build(); + MapArrivalsQuery query = MapArrivalsQuery.builder(List.of("40900")).build(); - assertThatThrownBy(() -> this.api.findByMapId(query)) + assertThatThrownBy(() -> this.api.findByMapIds(query)) .isInstanceOf(Cta4jArrivalsException.class) .hasMessage("An unknown error occurred.") .satisfies(e -> assertThat(((Cta4jArrivalsException) e).getErrorCode()) @@ -342,14 +418,14 @@ void findByMapId_throwsCta4jArrivalsException_withDefaultMessage_whenErrNmIsAbse } @Test - void findByMapId_throwsCta4jArrivalsException_whenServerReturnsErrorStatus() { + void findByMapIds_throwsCta4jArrivalsException_whenServerReturnsErrorStatus() { this.server.stubFor(get(urlPathEqualTo("/api/1.0/ttarrivals.aspx")) .willReturn(aResponse() .withStatus(500))); - MapArrivalsQuery query = MapArrivalsQuery.builder("40900").build(); + MapArrivalsQuery query = MapArrivalsQuery.builder(List.of("40900")).build(); - assertThatThrownBy(() -> this.api.findByMapId(query)) + assertThatThrownBy(() -> this.api.findByMapIds(query)) .isInstanceOf(Cta4jArrivalsException.class) .hasMessageContaining("status code: 500") .satisfies(e -> assertThat(e.getCause()).isNotNull()); diff --git a/src/test/java/com/cta4j/train/arrival/query/MapArrivalsQueryTest.java b/src/test/java/com/cta4j/train/arrival/query/MapArrivalsQueryTest.java index 1d04c154..83d90140 100644 --- a/src/test/java/com/cta4j/train/arrival/query/MapArrivalsQueryTest.java +++ b/src/test/java/com/cta4j/train/arrival/query/MapArrivalsQueryTest.java @@ -3,26 +3,36 @@ import com.cta4j.train.common.model.TrainLine; import org.junit.jupiter.api.Test; +import java.util.Collections; +import java.util.List; + import static org.assertj.core.api.Assertions.*; class MapArrivalsQueryTest { @Test void builder_buildsQueryWithOptionalParams() { - MapArrivalsQuery query = MapArrivalsQuery.builder("40900") + MapArrivalsQuery query = MapArrivalsQuery.builder(List.of("40900")) .line(TrainLine.RED) .maxResults(5) .build(); - assertThat(query.mapId()).isEqualTo("40900"); + assertThat(query.mapIds()).containsExactly("40900"); assertThat(query.line()).isEqualTo(TrainLine.RED); assertThat(query.maxResults()).isEqualTo(5); } + @Test + void builder_buildsQueryWithMultipleMapIds() { + MapArrivalsQuery query = MapArrivalsQuery.builder(List.of("40900", "40380", "40360")).build(); + + assertThat(query.mapIds()).containsExactly("40900", "40380", "40360"); + } + @Test void builder_buildsQueryWithNoOptionalParams() { - MapArrivalsQuery query = MapArrivalsQuery.builder("40900").build(); + MapArrivalsQuery query = MapArrivalsQuery.builder(List.of("40900")).build(); - assertThat(query.mapId()).isEqualTo("40900"); + assertThat(query.mapIds()).containsExactly("40900"); assertThat(query.line()).isNull(); assertThat(query.maxResults()).isNull(); } @@ -30,18 +40,43 @@ void builder_buildsQueryWithNoOptionalParams() { @Test void builder_throwsIllegalArgumentException_whenMaxResultsIsZero() { assertThatIllegalArgumentException().isThrownBy(() -> - MapArrivalsQuery.builder("40900").maxResults(0)); + MapArrivalsQuery.builder(List.of("40900")).maxResults(0)); } @Test void builder_throwsIllegalArgumentException_whenMaxResultsIsNegative() { assertThatIllegalArgumentException().isThrownBy(() -> - MapArrivalsQuery.builder("40900").maxResults(-1)); + MapArrivalsQuery.builder(List.of("40900")).maxResults(-1)); + } + + @Test + void builder_throwsIllegalArgumentException_whenMoreThanFourMapIds() { + List mapIds = Collections.nCopies(5, "40900"); + + assertThatIllegalArgumentException().isThrownBy(() -> + MapArrivalsQuery.builder(mapIds).build()); } @Test void constructor_throwsIllegalArgumentException_whenMaxResultsIsNotPositive() { assertThatIllegalArgumentException().isThrownBy(() -> - new MapArrivalsQuery("40900", null, 0)); + new MapArrivalsQuery(List.of("40900"), null, 0)); + } + + @Test + void constructor_throwsIllegalArgumentException_whenMoreThanFourMapIds() { + List mapIds = Collections.nCopies(5, "40900"); + + assertThatIllegalArgumentException().isThrownBy(() -> + new MapArrivalsQuery(mapIds, null, null)); + } + + @Test + void constructor_allowsExactlyFourMapIds() { + List mapIds = Collections.nCopies(4, "40900"); + + MapArrivalsQuery query = new MapArrivalsQuery(mapIds, null, null); + + assertThat(query.mapIds()).hasSize(4); } } diff --git a/src/test/java/com/cta4j/train/arrival/query/StopArrivalsQueryTest.java b/src/test/java/com/cta4j/train/arrival/query/StopArrivalsQueryTest.java index 1248a359..d6881a7a 100644 --- a/src/test/java/com/cta4j/train/arrival/query/StopArrivalsQueryTest.java +++ b/src/test/java/com/cta4j/train/arrival/query/StopArrivalsQueryTest.java @@ -3,26 +3,36 @@ import com.cta4j.train.common.model.TrainLine; import org.junit.jupiter.api.Test; +import java.util.Collections; +import java.util.List; + import static org.assertj.core.api.Assertions.*; class StopArrivalsQueryTest { @Test void builder_buildsQueryWithOptionalParams() { - StopArrivalsQuery query = StopArrivalsQuery.builder("30070") + StopArrivalsQuery query = StopArrivalsQuery.builder(List.of("30070")) .line(TrainLine.RED) .maxResults(5) .build(); - assertThat(query.stopId()).isEqualTo("30070"); + assertThat(query.stopIds()).containsExactly("30070"); assertThat(query.line()).isEqualTo(TrainLine.RED); assertThat(query.maxResults()).isEqualTo(5); } + @Test + void builder_buildsQueryWithMultipleStopIds() { + StopArrivalsQuery query = StopArrivalsQuery.builder(List.of("30070", "30071", "30375")).build(); + + assertThat(query.stopIds()).containsExactly("30070", "30071", "30375"); + } + @Test void builder_buildsQueryWithNoOptionalParams() { - StopArrivalsQuery query = StopArrivalsQuery.builder("30070").build(); + StopArrivalsQuery query = StopArrivalsQuery.builder(List.of("30070")).build(); - assertThat(query.stopId()).isEqualTo("30070"); + assertThat(query.stopIds()).containsExactly("30070"); assertThat(query.line()).isNull(); assertThat(query.maxResults()).isNull(); } @@ -30,18 +40,43 @@ void builder_buildsQueryWithNoOptionalParams() { @Test void builder_throwsIllegalArgumentException_whenMaxResultsIsZero() { assertThatIllegalArgumentException().isThrownBy(() -> - StopArrivalsQuery.builder("30070").maxResults(0)); + StopArrivalsQuery.builder(List.of("30070")).maxResults(0)); } @Test void builder_throwsIllegalArgumentException_whenMaxResultsIsNegative() { assertThatIllegalArgumentException().isThrownBy(() -> - StopArrivalsQuery.builder("30070").maxResults(-1)); + StopArrivalsQuery.builder(List.of("30070")).maxResults(-1)); + } + + @Test + void builder_throwsIllegalArgumentException_whenMoreThanFourStopIds() { + List stopIds = Collections.nCopies(5, "30070"); + + assertThatIllegalArgumentException().isThrownBy(() -> + StopArrivalsQuery.builder(stopIds).build()); } @Test void constructor_throwsIllegalArgumentException_whenMaxResultsIsNotPositive() { assertThatIllegalArgumentException().isThrownBy(() -> - new StopArrivalsQuery("30070", null, 0)); + new StopArrivalsQuery(List.of("30070"), null, 0)); + } + + @Test + void constructor_throwsIllegalArgumentException_whenMoreThanFourStopIds() { + List stopIds = Collections.nCopies(5, "30070"); + + assertThatIllegalArgumentException().isThrownBy(() -> + new StopArrivalsQuery(stopIds, null, null)); + } + + @Test + void constructor_allowsExactlyFourStopIds() { + List stopIds = Collections.nCopies(4, "30070"); + + StopArrivalsQuery query = new StopArrivalsQuery(stopIds, null, null); + + assertThat(query.stopIds()).hasSize(4); } } diff --git a/src/test/java/com/cta4j/train/common/internal/util/TrainApiUtilsTest.java b/src/test/java/com/cta4j/train/common/internal/util/TrainApiUtilsTest.java new file mode 100644 index 00000000..3b6312a9 --- /dev/null +++ b/src/test/java/com/cta4j/train/common/internal/util/TrainApiUtilsTest.java @@ -0,0 +1,34 @@ +package com.cta4j.train.common.internal.util; + +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.*; + +class TrainApiUtilsTest { + @Test + void requireMaxIds_doesNotThrow_whenIdsIsAtMax() { + List ids = List.of("1", "2", "3", "4"); + + assertThatCode(() -> TrainApiUtils.requireMaxIds(ids, "map")).doesNotThrowAnyException(); + } + + @Test + void requireMaxIds_throwsIllegalArgumentException_whenIdsExceedsMax() { + List ids = List.of("1", "2", "3", "4", "5"); + + assertThatIllegalArgumentException().isThrownBy(() -> TrainApiUtils.requireMaxIds(ids, "map")) + .withMessage("A maximum of 4 map IDs can be requested at once, but 5 were provided"); + } + + @Test + void requireMaxIds_throwsNullPointerException_whenIdsIsNull() { + assertThatNullPointerException().isThrownBy(() -> TrainApiUtils.requireMaxIds(null, "map")); + } + + @Test + void requireMaxIds_throwsNullPointerException_whenLabelIsNull() { + assertThatNullPointerException().isThrownBy(() -> TrainApiUtils.requireMaxIds(List.of(), null)); + } +} From df2f853119e098a1df394f69c27c15739301a76a Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Fri, 31 Jul 2026 22:52:32 -0500 Subject: [PATCH 48/60] Additional test coverage --- .../routestatus/RouteStatusApiImplTest.java | 22 ++++++++++++++++ .../internal/impl/SystemTimeApiImplTest.java | 15 +++++++++++ .../prediction/PredictionsApiImplTest.java | 15 +++++++++++ .../bus/vehicle/VehiclesApiImplTest.java | 15 +++++++++++ .../train/arrival/ArrivalsApiImplTest.java | 15 +++++++++++ .../arrival/query/MapArrivalsQueryTest.java | 25 +++++++++++++++++++ .../arrival/query/StopArrivalsQueryTest.java | 25 +++++++++++++++++++ .../resources/bus/time/empty-error-array.json | 5 ++++ 8 files changed, 137 insertions(+) create mode 100644 src/test/resources/bus/time/empty-error-array.json diff --git a/src/test/java/com/cta4j/alert/routestatus/RouteStatusApiImplTest.java b/src/test/java/com/cta4j/alert/routestatus/RouteStatusApiImplTest.java index b5c4cd1d..6d725f68 100644 --- a/src/test/java/com/cta4j/alert/routestatus/RouteStatusApiImplTest.java +++ b/src/test/java/com/cta4j/alert/routestatus/RouteStatusApiImplTest.java @@ -15,6 +15,7 @@ import org.junit.jupiter.api.Test; import tools.jackson.core.JacksonException; +import java.util.Arrays; import java.util.List; import static com.github.tomakehurst.wiremock.client.WireMock.*; @@ -266,6 +267,13 @@ void findByTypes_sendsTypeParameter_asCommaJoinedLowercase() { assertThat(statuses).hasSize(3); } + @Test + void findByTypes_throwsNullPointerException_whenTypesContainsNull() { + List withNull = Arrays.asList(ServiceType.BUS, null); + + assertThatNullPointerException().isThrownBy(() -> this.api.findByTypes(withNull)); + } + @Test void findByType_delegatesToFindByTypes() { this.server.stubFor(get(urlPathEqualTo("/api/1.0/routes.aspx")) @@ -318,6 +326,13 @@ void findByBusRouteIds_returnsRouteStatuses_whenResponseOmitsErrorEnvelope() { assertThat(status.status()).isEqualTo("Bus Stop Note"); } + @Test + void findByBusRouteIds_throwsNullPointerException_whenRouteIdsContainsNull() { + List withNull = Arrays.asList("22", null); + + assertThatNullPointerException().isThrownBy(() -> this.api.findByBusRouteIds(withNull)); + } + @Test void findByBusRouteIds_throwsIllegalArgumentException_whenRouteIdIsTrainLine() { assertThatIllegalArgumentException() @@ -357,6 +372,13 @@ void findByLines_returnsEmpty_whenInputIsEmpty() { this.server.verify(0, anyRequestedFor(anyUrl())); } + @Test + void findByLines_throwsNullPointerException_whenLinesContainsNull() { + List withNull = Arrays.asList(AlertTrainLine.RED, null); + + assertThatNullPointerException().isThrownBy(() -> this.api.findByLines(withNull)); + } + @Test void findByLines_sendsRouteidParameter_asCommaJoinedCodes() { this.server.stubFor(get(urlPathEqualTo("/api/1.0/routes.aspx")) diff --git a/src/test/java/com/cta4j/bus/common/internal/impl/SystemTimeApiImplTest.java b/src/test/java/com/cta4j/bus/common/internal/impl/SystemTimeApiImplTest.java index 2d4341d0..6432ed67 100644 --- a/src/test/java/com/cta4j/bus/common/internal/impl/SystemTimeApiImplTest.java +++ b/src/test/java/com/cta4j/bus/common/internal/impl/SystemTimeApiImplTest.java @@ -76,6 +76,21 @@ void systemTime_throwsCta4jBusException_whenResponseHasNoTimeAndNoErrors() { .isEqualTo(BusApiConstants.SYSTEM_TIME_ENDPOINT)); } + @Test + void systemTime_throwsCta4jBusException_whenErrorsIsExplicitlyEmptyArray() { + this.server.stubFor(get(urlPathEqualTo("/bustime/api/v3/gettime")) + .willReturn(aResponse() + .withStatus(200) + .withHeader("Content-Type", "application/json") + .withBody(TestFixtures.read("bus/time/empty-error-array.json")))); + + assertThatThrownBy(() -> this.api.systemTime()) + .isInstanceOf(Cta4jBusException.class) + .hasMessage("No system time data returned") + .satisfies(e -> assertThat(((Cta4jBusException) e).getEndpoint()) + .isEqualTo(BusApiConstants.SYSTEM_TIME_ENDPOINT)); + } + @Test void systemTime_throwsCta4jBusException_whenResponseIsNotJson() { this.server.stubFor(get(urlPathEqualTo("/bustime/api/v3/gettime")) diff --git a/src/test/java/com/cta4j/bus/prediction/PredictionsApiImplTest.java b/src/test/java/com/cta4j/bus/prediction/PredictionsApiImplTest.java index 8b94fc92..ea2cfac6 100644 --- a/src/test/java/com/cta4j/bus/prediction/PredictionsApiImplTest.java +++ b/src/test/java/com/cta4j/bus/prediction/PredictionsApiImplTest.java @@ -14,6 +14,7 @@ import org.junit.jupiter.api.Test; import tools.jackson.core.JacksonException; +import java.util.Arrays; import java.util.List; import static com.github.tomakehurst.wiremock.client.WireMock.*; @@ -302,4 +303,18 @@ void findByVehicleIds_collectionOverload_returnsPredictions_whenResponseContains assertThat(predictions).hasSize(1); } + + @Test + void findByStopIds_collectionOverload_throwsNullPointerException_whenStopIdsContainsNull() { + List withNull = Arrays.asList("456", null); + + assertThatNullPointerException().isThrownBy(() -> this.api.findByStopIds(withNull)); + } + + @Test + void findByVehicleIds_collectionOverload_throwsNullPointerException_whenVehicleIdsContainsNull() { + List withNull = Arrays.asList("509", null); + + assertThatNullPointerException().isThrownBy(() -> this.api.findByVehicleIds(withNull)); + } } diff --git a/src/test/java/com/cta4j/bus/vehicle/VehiclesApiImplTest.java b/src/test/java/com/cta4j/bus/vehicle/VehiclesApiImplTest.java index 34cc0d92..4226e20a 100644 --- a/src/test/java/com/cta4j/bus/vehicle/VehiclesApiImplTest.java +++ b/src/test/java/com/cta4j/bus/vehicle/VehiclesApiImplTest.java @@ -12,6 +12,7 @@ import org.junit.jupiter.api.Test; import tools.jackson.core.JacksonException; +import java.util.Arrays; import java.util.List; import java.util.Optional; @@ -168,6 +169,20 @@ void findByRouteIds_sendsRtParameter() { assertThat(vehicles).hasSize(1); } + @Test + void findByIds_throwsNullPointerException_whenIdsContainsNull() { + List withNull = Arrays.asList("509", null); + + assertThatNullPointerException().isThrownBy(() -> this.api.findByIds(withNull)); + } + + @Test + void findByRouteIds_throwsNullPointerException_whenRouteIdsContainsNull() { + List withNull = Arrays.asList("8", null); + + assertThatNullPointerException().isThrownBy(() -> this.api.findByRouteIds(withNull)); + } + @Test void findByIds_throwsIllegalArgumentException_whenTooManyVehicleIds() { List tooMany = List.of("1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"); diff --git a/src/test/java/com/cta4j/train/arrival/ArrivalsApiImplTest.java b/src/test/java/com/cta4j/train/arrival/ArrivalsApiImplTest.java index e4a6dc5e..f24ffd00 100644 --- a/src/test/java/com/cta4j/train/arrival/ArrivalsApiImplTest.java +++ b/src/test/java/com/cta4j/train/arrival/ArrivalsApiImplTest.java @@ -15,6 +15,7 @@ import org.junit.jupiter.api.Test; import tools.jackson.core.JacksonException; +import java.util.Arrays; import java.util.List; import static com.github.tomakehurst.wiremock.client.WireMock.*; @@ -305,6 +306,13 @@ void findByMapIds_collectionOverload_returnsArrivals_whenResponseContainsArrival assertThat(arrivals).hasSize(1); } + @Test + void findByMapIds_collectionOverload_throwsNullPointerException_whenMapIdsContainsNull() { + List withNull = Arrays.asList("40900", null); + + assertThatNullPointerException().isThrownBy(() -> this.api.findByMapIds(withNull)); + } + @Test void findByStopIds_collectionOverload_returnsArrivals_whenResponseContainsArrivals() { this.server.stubFor(get(urlPathEqualTo("/api/1.0/ttarrivals.aspx")) @@ -319,6 +327,13 @@ void findByStopIds_collectionOverload_returnsArrivals_whenResponseContainsArriva assertThat(arrivals).hasSize(1); } + @Test + void findByStopIds_collectionOverload_throwsNullPointerException_whenStopIdsContainsNull() { + List withNull = Arrays.asList("30070", null); + + assertThatNullPointerException().isThrownBy(() -> this.api.findByStopIds(withNull)); + } + @Test void findByMapId_stringOverload_returnsArrivals_whenResponseContainsArrivals() { this.server.stubFor(get(urlPathEqualTo("/api/1.0/ttarrivals.aspx")) diff --git a/src/test/java/com/cta4j/train/arrival/query/MapArrivalsQueryTest.java b/src/test/java/com/cta4j/train/arrival/query/MapArrivalsQueryTest.java index 83d90140..88c6d03c 100644 --- a/src/test/java/com/cta4j/train/arrival/query/MapArrivalsQueryTest.java +++ b/src/test/java/com/cta4j/train/arrival/query/MapArrivalsQueryTest.java @@ -3,6 +3,7 @@ import com.cta4j.train.common.model.TrainLine; import org.junit.jupiter.api.Test; +import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -37,6 +38,18 @@ void builder_buildsQueryWithNoOptionalParams() { assertThat(query.maxResults()).isNull(); } + @Test + void builder_throwsNullPointerException_whenMapIdsIsNull() { + assertThatNullPointerException().isThrownBy(() -> MapArrivalsQuery.builder(null)); + } + + @Test + void builder_throwsNullPointerException_whenMapIdsContainsNull() { + List withNull = Arrays.asList("40900", null); + + assertThatNullPointerException().isThrownBy(() -> MapArrivalsQuery.builder(withNull)); + } + @Test void builder_throwsIllegalArgumentException_whenMaxResultsIsZero() { assertThatIllegalArgumentException().isThrownBy(() -> @@ -57,6 +70,18 @@ void builder_throwsIllegalArgumentException_whenMoreThanFourMapIds() { MapArrivalsQuery.builder(mapIds).build()); } + @Test + void constructor_throwsNullPointerException_whenMapIdsIsNull() { + assertThatNullPointerException().isThrownBy(() -> new MapArrivalsQuery(null, null, null)); + } + + @Test + void constructor_throwsNullPointerException_whenMapIdsContainsNull() { + List withNull = Arrays.asList("40900", null); + + assertThatNullPointerException().isThrownBy(() -> new MapArrivalsQuery(withNull, null, null)); + } + @Test void constructor_throwsIllegalArgumentException_whenMaxResultsIsNotPositive() { assertThatIllegalArgumentException().isThrownBy(() -> diff --git a/src/test/java/com/cta4j/train/arrival/query/StopArrivalsQueryTest.java b/src/test/java/com/cta4j/train/arrival/query/StopArrivalsQueryTest.java index d6881a7a..1c3d7e5f 100644 --- a/src/test/java/com/cta4j/train/arrival/query/StopArrivalsQueryTest.java +++ b/src/test/java/com/cta4j/train/arrival/query/StopArrivalsQueryTest.java @@ -3,6 +3,7 @@ import com.cta4j.train.common.model.TrainLine; import org.junit.jupiter.api.Test; +import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -37,6 +38,18 @@ void builder_buildsQueryWithNoOptionalParams() { assertThat(query.maxResults()).isNull(); } + @Test + void builder_throwsNullPointerException_whenStopIdsIsNull() { + assertThatNullPointerException().isThrownBy(() -> StopArrivalsQuery.builder(null)); + } + + @Test + void builder_throwsNullPointerException_whenStopIdsContainsNull() { + List withNull = Arrays.asList("30070", null); + + assertThatNullPointerException().isThrownBy(() -> StopArrivalsQuery.builder(withNull)); + } + @Test void builder_throwsIllegalArgumentException_whenMaxResultsIsZero() { assertThatIllegalArgumentException().isThrownBy(() -> @@ -57,6 +70,18 @@ void builder_throwsIllegalArgumentException_whenMoreThanFourStopIds() { StopArrivalsQuery.builder(stopIds).build()); } + @Test + void constructor_throwsNullPointerException_whenStopIdsIsNull() { + assertThatNullPointerException().isThrownBy(() -> new StopArrivalsQuery(null, null, null)); + } + + @Test + void constructor_throwsNullPointerException_whenStopIdsContainsNull() { + List withNull = Arrays.asList("30070", null); + + assertThatNullPointerException().isThrownBy(() -> new StopArrivalsQuery(withNull, null, null)); + } + @Test void constructor_throwsIllegalArgumentException_whenMaxResultsIsNotPositive() { assertThatIllegalArgumentException().isThrownBy(() -> diff --git a/src/test/resources/bus/time/empty-error-array.json b/src/test/resources/bus/time/empty-error-array.json new file mode 100644 index 00000000..008e8f54 --- /dev/null +++ b/src/test/resources/bus/time/empty-error-array.json @@ -0,0 +1,5 @@ +{ + "bustime-response": { + "error": [] + } +} \ No newline at end of file From 6d7b9f5d1c17d108448a133dde8438e317387dbd Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Fri, 31 Jul 2026 23:51:48 -0500 Subject: [PATCH 49/60] Javadoc fixes --- src/main/java/com/cta4j/alert/AlertApi.java | 8 ++++---- src/main/java/com/cta4j/bus/BusApi.java | 8 ++++---- .../bus/prediction/query/VehiclePredictionsQuery.java | 2 +- src/main/java/com/cta4j/train/TrainApi.java | 8 ++++---- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/cta4j/alert/AlertApi.java b/src/main/java/com/cta4j/alert/AlertApi.java index f1112dd0..8e8efee9 100644 --- a/src/main/java/com/cta4j/alert/AlertApi.java +++ b/src/main/java/com/cta4j/alert/AlertApi.java @@ -35,15 +35,15 @@ interface Builder { /// @throws NullPointerException if `host` is `null` Builder host(String host); - /// Builds a configured [AlertApi] instance. + /// Builds a configured `AlertApi` instance. /// - /// @return a new [AlertApi] + /// @return a new `AlertApi` AlertApi build(); } - /// Creates a new [Builder] for constructing a [AlertApi]. + /// Creates a new `Builder` for constructing an `AlertApi`. /// - /// @return a new [Builder] + /// @return a new `Builder` static Builder builder() { return new AlertApiImpl.BuilderImpl(); } diff --git a/src/main/java/com/cta4j/bus/BusApi.java b/src/main/java/com/cta4j/bus/BusApi.java index 96962cd8..d701ca99 100644 --- a/src/main/java/com/cta4j/bus/BusApi.java +++ b/src/main/java/com/cta4j/bus/BusApi.java @@ -81,16 +81,16 @@ interface Builder { /// @throws NullPointerException if `host` is `null` Builder host(String host); - /// Builds a configured [BusApi] instance. + /// Builds a configured `BusApi` instance. /// - /// @return a new [BusApi] + /// @return a new `BusApi` BusApi build(); } - /// Creates a new [Builder] for constructing a [BusApi]. + /// Creates a new `Builder` for constructing a `BusApi`. /// /// @param apiKey the CTA Bus Tracker API key - /// @return a new [Builder] + /// @return a new `Builder` /// @throws NullPointerException if `apiKey` is `null` static Builder builder(String apiKey) { Objects.requireNonNull(apiKey); diff --git a/src/main/java/com/cta4j/bus/prediction/query/VehiclePredictionsQuery.java b/src/main/java/com/cta4j/bus/prediction/query/VehiclePredictionsQuery.java index c242e162..7fc22995 100644 --- a/src/main/java/com/cta4j/bus/prediction/query/VehiclePredictionsQuery.java +++ b/src/main/java/com/cta4j/bus/prediction/query/VehiclePredictionsQuery.java @@ -45,7 +45,7 @@ public static Builder builder(Collection vehicleIds) { return new Builder(vehicleIds); } - /// Builder for `VehiclePredictionsQuery`. + /// A builder for `VehiclePredictionsQuery`. public static final class Builder { private final List vehicleIds; diff --git a/src/main/java/com/cta4j/train/TrainApi.java b/src/main/java/com/cta4j/train/TrainApi.java index 4b1562da..ad3ea682 100644 --- a/src/main/java/com/cta4j/train/TrainApi.java +++ b/src/main/java/com/cta4j/train/TrainApi.java @@ -58,16 +58,16 @@ interface Builder { /// @throws NullPointerException if `stationsUrl` is `null` Builder stationsUrl(String stationsUrl); - /// Builds a configured [TrainApi] instance. + /// Builds a configured `TrainApi` instance. /// - /// @return a new [TrainApi] + /// @return a new `TrainApi` TrainApi build(); } - /// Creates a new [Builder] for constructing a [TrainApi]. + /// Creates a new `Builder` for constructing a `TrainApi`. /// /// @param apiKey the CTA Train Tracker API key - /// @return a new [Builder] + /// @return a new `Builder` /// @throws NullPointerException if `apiKey` is `null` static Builder builder(String apiKey) { Objects.requireNonNull(apiKey); From 10ee69fd2c3fdec638d82275bdfca7b8aefdb343 Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Sat, 1 Aug 2026 02:10:45 -0500 Subject: [PATCH 50/60] Nullable Javadoc fix and CLAUDE.md update --- CLAUDE.md | 15 +++++++++++---- .../arrival/exception/Cta4jArrivalsException.java | 2 +- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 3b318dbf..308c3e57 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -70,9 +70,11 @@ Shared types live in `bus/common/` or `train/common/`. Cross-cutting types (geo, `find*` methods return an empty `List` (or `Optional.empty()`) for not-found; they never throw for missing resources. -## Annotation Ordering +## Annotations -Always apply annotations in this order: **Jackson/framework → `@ApiStatus.Internal` → `@NullMarked`** +- Annotations always stack one per line above the declaration. +- They should adhere to the following order: Jackson/framework → `@ApiStatus.Internal` → `@NullMarked`. +- This applies to classes, interfaces, records, fields, and methods alike. ```java @JsonIgnoreProperties(ignoreUnknown = true) @@ -81,6 +83,11 @@ Always apply annotations in this order: **Jackson/framework → `@ApiStatus.Inte public record CtaFoo(...) { } ``` +- **Exception — Record components:** A component with a single annotation may keep it inline with the type + (`@Nullable String foo`) instead of stacking. Two or more annotations on a component still stack one per + line above the type, and when any component in a record stacks, blank-line-separate every component in + that record (see `CtaLocation` vs. the single-annotation `CtaStation`). + ## Null Safety - All classes, records, interfaces, and enums in `src/main` must have `@NullMarked`. @@ -122,13 +129,13 @@ References: `TransitMode`, `DynamicAction`, `TrainLine`) and numeric/status-code accessors on error-code enums or exceptions alike. Do not use "Gets the X" to distinguish the two. -- **Builder setter `@return`:** Always backticked, "this `Builder` instance" — - never the unbacked "this builder instance" variant. - **Builder creator methods:** One template for every builder, top-level client builders and query-parameter builders alike — no terser variant. The static `builder(...)` method: "Creates a new `Builder` for constructing a/an `X`.", `@return` tag "a new `Builder`". The `build()` method: "Builds a configured `X` instance.", `@return` tag "a new `X`". +- **Builder setter `@return`:** Always backticked, "this `Builder` instance" — + never the unbacked "this builder instance" variant. - **Tag order:** `@param` → `@return` → `@deprecated` → `@since` → `@throws` → `@see`. - **@param / @throws descriptions:** Lowercase phrase, no trailing period. diff --git a/src/main/java/com/cta4j/train/arrival/exception/Cta4jArrivalsException.java b/src/main/java/com/cta4j/train/arrival/exception/Cta4jArrivalsException.java index 6e37b99f..b88c896d 100644 --- a/src/main/java/com/cta4j/train/arrival/exception/Cta4jArrivalsException.java +++ b/src/main/java/com/cta4j/train/arrival/exception/Cta4jArrivalsException.java @@ -33,7 +33,7 @@ public Cta4jArrivalsException(String message, int rawErrorCode) { /// Returns the error code associated with this exception, if available. /// - /// @return the error code, if available + /// @return the error code, or `null` if not available public @Nullable ArrivalsErrorCode getErrorCode() { return this.errorCode; } From f52a22fa72bfcbbee6939ccdaec9dff8a777ed5b Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Sat, 1 Aug 2026 12:39:35 -0500 Subject: [PATCH 51/60] Remove unknown train line and make it a nullable field --- .../train/common/internal/mapper/Qualifiers.java | 14 ++++++++++++-- .../com/cta4j/train/common/model/Arrival.java | 12 +++++++----- .../com/cta4j/train/common/model/TrainLine.java | 16 +++++----------- .../train/location/model/TrainLocations.java | 10 +++++----- .../cta4j/train/common/ArrivalMapperTest.java | 4 ++-- .../cta4j/train/common/TrainQualifiersTest.java | 4 ++-- .../cta4j/train/common/model/TrainLineTest.java | 12 +++--------- 7 files changed, 36 insertions(+), 36 deletions(-) diff --git a/src/main/java/com/cta4j/train/common/internal/mapper/Qualifiers.java b/src/main/java/com/cta4j/train/common/internal/mapper/Qualifiers.java index 6368a4e4..a4e01da6 100644 --- a/src/main/java/com/cta4j/train/common/internal/mapper/Qualifiers.java +++ b/src/main/java/com/cta4j/train/common/internal/mapper/Qualifiers.java @@ -14,6 +14,8 @@ import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; import org.mapstruct.Named; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import tools.jackson.core.JacksonException; import tools.jackson.databind.json.JsonMapper; @@ -28,6 +30,8 @@ @ApiStatus.Internal @NullMarked public final class Qualifiers { + private static final Logger log = LoggerFactory.getLogger(Qualifiers.class); + private static final DateTimeFormatter TIMESTAMP_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"); private static final ZoneId CHICAGO_ZONE_ID = ZoneId.of("America/Chicago"); @@ -104,10 +108,16 @@ public static Set mapTrainLines(CtaStation station) { } @Named("mapLine") - public static TrainLine mapLine(String line) { + public static @Nullable TrainLine mapLine(String line) { Objects.requireNonNull(line); - return TrainLine.fromCode(line); + TrainLine trainLine = TrainLine.fromCode(line); + + if (trainLine == null) { + log.warn("Unknown train line code: {}", line); + } + + return trainLine; } @Named("mapTimestamp") diff --git a/src/main/java/com/cta4j/train/common/model/Arrival.java b/src/main/java/com/cta4j/train/common/model/Arrival.java index f06252af..ff241463 100644 --- a/src/main/java/com/cta4j/train/common/model/Arrival.java +++ b/src/main/java/com/cta4j/train/common/model/Arrival.java @@ -1,17 +1,20 @@ package com.cta4j.train.common.model; import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import java.time.Instant; import java.util.Objects; /// Represents a train arrival. /// +/// **NOTE:** The CTA will occasionally send a train line code that can't be resolved, in which case `line` is `null`. +/// /// @param stationId the unique identifier of the station for which this arrival was generated /// @param stationName the display name of the station for which this arrival was generated /// @param stopId the unique identifier of the stop for which this arrival was generated /// @param stopDescription the display name of the stop for which this arrival was generated -/// @param line the train line associated with this arrival +/// @param line the train line associated with this arrival, if applicable /// @param destinationStationId the unique identifier of the destination station for this arrival /// @param destinationName the display name of the destination station for this arrival /// @param predictionTime the date and time (UTC) this arrival was generated @@ -28,7 +31,7 @@ public record Arrival( String stationName, String stopId, String stopDescription, - TrainLine line, + @Nullable TrainLine line, String destinationStationId, String destinationName, Instant predictionTime, @@ -45,7 +48,7 @@ public record Arrival( /// @param stationName the display name of the station for which the arrival was generated /// @param stopId the unique identifier of the stop for which the arrival was generated /// @param stopDescription the display name of the stop for which the arrival was generated - /// @param line the train line associated with the arrival + /// @param line the train line associated with the arrival, if applicable /// @param destinationStationId the unique identifier of the destination station for the arrival /// @param destinationName the display name of the destination station for the arrival /// @param predictionTime the date and time (UTC) the arrival was generated @@ -56,14 +59,13 @@ public record Arrival( /// @param delayed whether the train associated with the arrival is currently delayed /// @param fault whether the train associated with the arrival is currently experiencing a fault /// @param metadata the metadata associated with the arrival - /// @throws NullPointerException if `stationId`, `stationName`, `stopId`, `stopDescription`, `line`, + /// @throws NullPointerException if `stationId`, `stationName`, `stopId`, `stopDescription`, /// `destinationStationId`, `destinationName`, `predictionTime`, `arrivalTime`, or `metadata` is `null` public Arrival { Objects.requireNonNull(stationId); Objects.requireNonNull(stationName); Objects.requireNonNull(stopId); Objects.requireNonNull(stopDescription); - Objects.requireNonNull(line); Objects.requireNonNull(destinationStationId); Objects.requireNonNull(destinationName); Objects.requireNonNull(predictionTime); diff --git a/src/main/java/com/cta4j/train/common/model/TrainLine.java b/src/main/java/com/cta4j/train/common/model/TrainLine.java index b0537b42..4d107ac8 100644 --- a/src/main/java/com/cta4j/train/common/model/TrainLine.java +++ b/src/main/java/com/cta4j/train/common/model/TrainLine.java @@ -1,6 +1,7 @@ package com.cta4j.train.common.model; import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import java.util.Objects; @@ -29,13 +30,7 @@ public enum TrainLine { PINK("Pink", "#E27EA6"), /// Indicates the Yellow Line. - YELLOW("Y", "#F9E300"), - - /// Indicates that the train line is unknown. - /// - /// The CTA will occasionally report an unknown line for an arrival, even though "unknown" is not itself a valid - /// train line. - UNKNOWN("N/A", "#808080"); + YELLOW("Y", "#F9E300"); private final String code; private final String colorHex; @@ -62,9 +57,8 @@ public String getColorHex() { /// Returns the `TrainLine` corresponding to the given code. /// /// @param code the CTA code of the train line (case-insensitive, may include "LINE" suffix) - /// @return the corresponding `TrainLine`, or `TrainLine.UNKNOWN` if the code does not correspond to any known - /// train line - public static TrainLine fromCode(String code) { + /// @return the corresponding `TrainLine`, or `null` if the code does not correspond to any known train line + public static @Nullable TrainLine fromCode(String code) { Objects.requireNonNull(code); return switch (code.toUpperCase()) { @@ -76,7 +70,7 @@ public static TrainLine fromCode(String code) { case "P", "PURPLE LINE" -> TrainLine.PURPLE; case "PINK", "PINK LINE" -> TrainLine.PINK; case "Y", "YELLOW LINE" -> TrainLine.YELLOW; - default -> TrainLine.UNKNOWN; + default -> null; }; } } diff --git a/src/main/java/com/cta4j/train/location/model/TrainLocations.java b/src/main/java/com/cta4j/train/location/model/TrainLocations.java index 0729d028..8f7a66ac 100644 --- a/src/main/java/com/cta4j/train/location/model/TrainLocations.java +++ b/src/main/java/com/cta4j/train/location/model/TrainLocations.java @@ -2,26 +2,26 @@ import com.cta4j.train.common.model.TrainLine; import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import java.util.List; import java.util.Objects; /// Represents the locations of all trains on a route. /// -/// @param line the train line associated with these locations +/// @param line the train line associated with these locations, if applicable /// @param trains the [List] of [LocationTrain]s for this train line @NullMarked public record TrainLocations( - TrainLine line, + @Nullable TrainLine line, List trains ) { /// Constructs a `TrainLocations`. /// - /// @param line the train line associated with the locations + /// @param line the train line associated with the locations, if applicable /// @param trains the [List] of [LocationTrain]s for the train line - /// @throws NullPointerException if `line` or `trains` is `null`, or if any element of `trains` is `null` + /// @throws NullPointerException if `trains` is `null`, or if any element of `trains` is `null` public TrainLocations { - Objects.requireNonNull(line); Objects.requireNonNull(trains); trains = List.copyOf(trains); diff --git a/src/test/java/com/cta4j/train/common/ArrivalMapperTest.java b/src/test/java/com/cta4j/train/common/ArrivalMapperTest.java index 8c30b690..287a9175 100644 --- a/src/test/java/com/cta4j/train/common/ArrivalMapperTest.java +++ b/src/test/java/com/cta4j/train/common/ArrivalMapperTest.java @@ -77,7 +77,7 @@ void toDomain_mapsApproachingTrue() { } @Test - void toDomain_mapsUnknownLine_whenRtIsNotApplicable() { + void toDomain_mapsNullLine_whenRtIsNotApplicable() { CtaArrival wire = new CtaArrival( "40100", "30070", "Howard", "Service toward O'Hare", @@ -92,6 +92,6 @@ void toDomain_mapsUnknownLine_whenRtIsNotApplicable() { Arrival arrival = ArrivalMapper.INSTANCE.toDomain(wire); - assertThat(arrival.line()).isEqualTo(TrainLine.UNKNOWN); + assertThat(arrival.line()).isNull(); } } diff --git a/src/test/java/com/cta4j/train/common/TrainQualifiersTest.java b/src/test/java/com/cta4j/train/common/TrainQualifiersTest.java index b9e7fec5..85177b2c 100644 --- a/src/test/java/com/cta4j/train/common/TrainQualifiersTest.java +++ b/src/test/java/com/cta4j/train/common/TrainQualifiersTest.java @@ -136,8 +136,8 @@ void mapLine_returnsRed_whenLineIsRed() { } @Test - void mapLine_returnsUnknown_whenLineIsNotApplicable() { - assertThat(Qualifiers.mapLine("N/A")).isEqualTo(TrainLine.UNKNOWN); + void mapLine_returnsNull_whenLineIsNotApplicable() { + assertThat(Qualifiers.mapLine("N/A")).isNull(); } @Test diff --git a/src/test/java/com/cta4j/train/common/model/TrainLineTest.java b/src/test/java/com/cta4j/train/common/model/TrainLineTest.java index 6d5f64a8..38cabd24 100644 --- a/src/test/java/com/cta4j/train/common/model/TrainLineTest.java +++ b/src/test/java/com/cta4j/train/common/model/TrainLineTest.java @@ -26,9 +26,9 @@ void fromCode_returnsCorrectValues() { } @Test - void fromCode_returnsUnknown_whenCodeIsUnrecognized() { - assertThat(TrainLine.fromCode("N/A")).isEqualTo(TrainLine.UNKNOWN); - assertThat(TrainLine.fromCode("Unknown")).isEqualTo(TrainLine.UNKNOWN); + void fromCode_returnsNull_whenCodeIsUnrecognized() { + assertThat(TrainLine.fromCode("N/A")).isNull(); + assertThat(TrainLine.fromCode("Unknown")).isNull(); } @Test @@ -36,10 +36,4 @@ void getCode_andGetColorHex_returnValues() { assertThat(TrainLine.RED.getCode()).isEqualTo("Red"); assertThat(TrainLine.RED.getColorHex()).isEqualTo("#C60C30"); } - - @Test - void getCode_andGetColorHex_returnValues_forUnknown() { - assertThat(TrainLine.UNKNOWN.getCode()).isEqualTo("N/A"); - assertThat(TrainLine.UNKNOWN.getColorHex()).isEqualTo("#808080"); - } } From 2f155f2ecf691bdfb0f9415618a058abb1e6daa4 Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Sat, 1 Aug 2026 12:44:33 -0500 Subject: [PATCH 52/60] Fix exception Javadoc --- .../java/com/cta4j/common/exception/Cta4jException.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/cta4j/common/exception/Cta4jException.java b/src/main/java/com/cta4j/common/exception/Cta4jException.java index bc0a6c28..fd044cfa 100644 --- a/src/main/java/com/cta4j/common/exception/Cta4jException.java +++ b/src/main/java/com/cta4j/common/exception/Cta4jException.java @@ -12,7 +12,7 @@ public class Cta4jException extends RuntimeException { /// Constructs a `Cta4jException`. /// /// @param message the detail message - /// @param endpoint the URL of the API endpoint associated with the exception + /// @param endpoint the API endpoint associated with the exception /// @throws NullPointerException if `endpoint` is `null` public Cta4jException(String message, String endpoint) { super(message); @@ -23,7 +23,7 @@ public Cta4jException(String message, String endpoint) { /// Constructs a `Cta4jException`. /// /// @param message the detail message - /// @param endpoint the URL of the API endpoint associated with the exception + /// @param endpoint the API endpoint associated with the exception /// @param cause the cause of the exception /// @throws NullPointerException if `endpoint` is `null` public Cta4jException(String message, String endpoint, Throwable cause) { @@ -32,9 +32,9 @@ public Cta4jException(String message, String endpoint, Throwable cause) { this.endpoint = Objects.requireNonNull(endpoint); } - /// Returns the URL of the API endpoint associated with this exception. + /// Returns the API endpoint associated with this exception. /// - /// @return the endpoint URL + /// @return the endpoint public String getEndpoint() { return this.endpoint; } From 849705045cd21a0a464360ed5518d7246e0e3a93 Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Sat, 1 Aug 2026 14:22:12 -0500 Subject: [PATCH 53/60] Update CHANGELOG and CLAUDE.md for 7.0.0 release; add AlertApi and related features --- CHANGELOG.md | 44 ++++++++++++++++++- CLAUDE.md | 23 +++++++--- .../com/cta4j/bus/vehicle/VehiclesApi.java | 20 ++++----- .../internal/impl/VehiclesApiImpl.java | 12 ++--- 4 files changed, 75 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 67273485..84e80a98 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,47 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [7.0.0] - 2026-08-01 + +### Added + +- A new `AlertApi` composed entry point for the CTA Customer Alerts API, unauthenticated and constructed via + `AlertApi.builder().build()` (no API key required). + - `RouteStatusApi`, exposing route/line service status (`list()`, `findByTypes`, `findByBusRouteIds`, + `findByLines`, `findByStationId`). + - `DetailedAlertsApi`, exposing detailed service alerts (`list(AlertsQuery)`, plus filtered lookups by bus + route IDs, train lines, and station IDs). + - New domain models (`RouteStatus`, `Alert`, `ImpactedService`, `Severity`, `AlertTrainLine`, `ServiceType`), + feature-specific exceptions (`Cta4jRouteStatusException`, `Cta4jDetailedAlertsException`, both extending + the new shared `Cta4jAlertException`), and error-code enums (`RouteStatusErrorCode`, + `DetailedAlertsErrorCode`). +- `ArrivalsApi.findByMapIds`/`findByStopIds` — multi-value lookups accepting up to 4 map/stop IDs per request + (`MapArrivalsQuery`/`StopArrivalsQuery`, plus `Collection` convenience overloads), alongside the + existing single-ID `findByMapId`/`findByStopId`. + +### Changed + +- Renamed `StopsPredictionsQuery`/`VehiclesPredictionsQuery` to `StopPredictionsQuery`/`VehiclePredictionsQuery` + for consistency with the rest of the query-builder naming. +- `TrainLine.fromCode(String)` no longer throws `IllegalArgumentException` for an unrecognized code; it now + returns `null` and logs a warning, matching the `@Nullable`-based degrade pattern used elsewhere in the SDK. + +### Breaking Changes ⚠️ + +- **Builder constructors are now `private`** on all query-parameter builders (`StopPredictionsQuery`, + `VehiclePredictionsQuery`, `MapArrivalsQuery`, `StopArrivalsQuery`, `AlertsQuery`, `BusRouteAlertsQuery`, + `LineAlertsQuery`, `StationAlertsQuery`). Construct instances via the static `builder(...)` factory method + only. +- `MapArrivalQuery`/`StopArrivalQuery` have been renamed and reshaped to `MapArrivalsQuery`/`StopArrivalsQuery`: + the single `String mapId`/`stopId` component is now a `List mapIds`/`stopIds` component (max 4 IDs). +- `ArrivalsApi.findByMapId(MapArrivalQuery)`/`findByStopId(StopArrivalQuery)` have been renamed to + `findByMapIds(MapArrivalsQuery)`/`findByStopIds(StopArrivalsQuery)` to match the new query types. +- `MapArrivalsQuery.Builder.maxResults`/`StopArrivalsQuery.Builder.maxResults` now take `int` instead of + `Integer`; passing `null` no longer compiles. +- `Arrival.line` is now `@Nullable`, a direct consequence of `TrainLine.fromCode` no longer throwing — code that + assumed `line()` was always non-null must add a null check. +- `LocationsApi.findAll()` has been renamed to `LocationsApi.list()`. + ## [6.0.0] - 2026-07-05 ### Added @@ -267,7 +308,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `TrainClient` class with methods to interact with CTA Train API. - `BusClient` class with methods to interact with CTA Bus API. -[Unreleased]: https://github.com/lbkulinski/cta4j-java-sdk/compare/v6.0.0...HEAD +[Unreleased]: https://github.com/lbkulinski/cta4j-java-sdk/compare/v7.0.0...HEAD +[7.0.0]: https://github.com/lbkulinski/cta4j-java-sdk/compare/v6.0.0...v7.0.0 [6.0.0]: https://github.com/lbkulinski/cta4j-java-sdk/compare/v5.0.0...v6.0.0 [5.0.0]: https://github.com/lbkulinski/cta4j-java-sdk/compare/v4.1.0...v5.0.0 [4.1.0]: https://github.com/lbkulinski/cta4j-java-sdk/compare/v4.0.3...v4.1.0 diff --git a/CLAUDE.md b/CLAUDE.md index 308c3e57..20ed0b4c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -2,16 +2,17 @@ ## Project Overview -Java SDK for the CTA Bus Tracker and Train Tracker APIs. Published to Maven Central. Consumers instantiate `BusApi` or `TrainApi` via their respective builders and access sub-APIs from there. +Java SDK for the CTA Bus Tracker, Train Tracker, and Customer Alerts APIs. Published to Maven Central. Consumers instantiate `BusApi`, `TrainApi`, or `AlertApi` via their respective builders and access sub-APIs from there. ## Entry Points ```java BusApi busApi = BusApi.builder("apiKey").build(); TrainApi trainApi = TrainApi.builder("apiKey").build(); +AlertApi alertApi = AlertApi.builder().build(); ``` -Both builders accept an optional `.host(String)` override; `TrainApi.Builder` also accepts `.stationsUrl(String)`. +`BusApi`/`TrainApi` builders require an API key and accept an optional `.host(String)` override; `TrainApi.Builder` also accepts `.stationsUrl(String)`. The CTA Customer Alerts API is unauthenticated, so `AlertApi.builder()` takes no API key and only accepts an optional `.host(String)` override. ## API Surface @@ -32,9 +33,13 @@ Both builders accept an optional `.host(String)` override; `TrainApi.Builder` al - `follow()` — `FollowApi` - `locations()` — `LocationsApi` +**Alert (`AlertApi`)** +- `routeStatus()` — `RouteStatusApi` +- `detailedAlerts()` — `DetailedAlertsApi` + ## Package Layout -Transit type (`bus`, `train`) then feature then layer: +Transit type (`bus`, `train`, `alert`) then feature then layer: ``` com.cta4j.bus.route/ @@ -46,28 +51,32 @@ com.cta4j.bus.route/ mapper/RouteMapper.java ← MapStruct mapper ``` -Shared types live in `bus/common/` or `train/common/`. Cross-cutting types (geo, exceptions) live in `common/` or `exception/`. +Shared types live in `bus/common/`, `train/common/`, or `alert/common/`. Cross-cutting types (geo, exceptions) live in `common/` or `exception/`. ## Wire Layer Conventions - All wire records are `@ApiStatus.Internal` and not part of the public API. - **Bus**: each feature has a typed `CtaBustimeResponse` record (envelope field `bustimeResponse`, mapped from `"bustime-response"`) with `@Nullable` fields for both the data list and a typed `CtaError` list. Each `CtaError` implements `CtaError` and overrides `notFound()` using its own typed fields (e.g. `rt`, `stpid`, `vid`) to identify which input caused the error — do not collapse these into a generic map. - **Train**: there is no shared `CtaError`-style record. Each feature's wire response record carries `errCd`/`errNm` fields directly; `errCd` is parsed to an `int` and mapped via `ErrorCode.fromCode(int)` to a feature-specific enum (e.g. `ArrivalsErrorCode`) whose constants identify resource-specific ("not found") codes. +- **Alert**: like Train, there is no shared `CtaError`-style record, and each feature's error shape is its own — do not assume `RouteStatus` and `DetailedAlerts` match each other. `CtaRoutes` (route status) carries `ErrorCode`/`ErrorMessage` as `List`, since the CTA API can return multiple distinct codes for a single request; `CtaAlerts` (detailed alerts) carries a single non-nullable `ErrorCode` `String` and a `@Nullable ErrorMessage` `String`. Both map their code(s) via a feature-specific `ErrorCode.fromCode(int)` enum (`RouteStatusErrorCode`, `DetailedAlertsErrorCode`), following the Train pattern. - All wire records use `@JsonIgnoreProperties(ignoreUnknown = true)`. -- The outer envelope is `CtaResponse`, with a `bustimeResponse` field (bus) or a `ctatt` field (train). +- **Bus and Train** share a single generic `CtaResponse` record per module (`bus/common/internal/wire/CtaResponse`, `train/common/internal/wire/CtaResponse`) with a fixed field name — `bustimeResponse` (bus) or `ctatt` (train). +- **Alert** has no shared generic envelope type; each feature declares its own concretely-typed response record with its own field name (e.g. `CtaRouteStatusResponse.ctaRoutes` mapped from `"CTARoutes"`, `CtaDetailedAlertsResponse.ctaAlerts` mapped from `"CTAAlerts"`). ## Error Handling Pattern **Bus** `*ApiImpl` classes returning a `List` follow this pattern in `makeRequest`: 1. If the data list is non-null and non-empty → map and return it. -2. Otherwise, call `ApiUtils.checkErrors(errors, endpoint)` (`bus/common/internal/util/ApiUtils`): it logs a warn and returns if the error list is null/empty, returns if every error's `notFound()` is `true`, or throws `Cta4jBusException` otherwise. +2. Otherwise, call `BusApiUtils.checkErrors(errors, endpoint)` (`bus/common/internal/util/BusApiUtils`): it logs a warn and returns if the error list is null/empty, returns if every error's `notFound()` is `true`, or throws `Cta4jBusException` otherwise. 3. Return `List.of()`. -`SystemTimeApiImpl` is the one exception: it returns a single `Instant`, not a `List`, so a missing value has no valid "empty" result — it throws directly instead of calling `ApiUtils.checkErrors`. +`SystemTimeApiImpl` is the one exception: it returns a single `Instant`, not a `List`, so a missing value has no valid "empty" result — it throws directly instead of calling `BusApiUtils.checkErrors`. **Train** `*ApiImpl` classes follow a related but distinct pattern (no shared helper — each impl inlines it with its own exception type): parse `errCd` to the feature's `*ErrorCode` enum; if it's a resource-specific not-found code → return an empty result (or `Optional.empty()`); if it isn't `OK` → throw the feature-specific exception (e.g. `Cta4jArrivalsException`) using `errNm` as the message, falling back to a default message when `errNm` is `null` or blank. +**Alert** `*ApiImpl` classes follow the same inlined, no-shared-helper pattern as Train, but each feature's `errCd`/`errNm` shape differs (see Wire Layer Conventions above): `RouteStatusApiImpl` reads the first of a possibly-multi-value error-code list (logging a warn if more than one distinct code is present) and falls back to `"An unknown error occurred."` when the message is null/blank; `DetailedAlertsApiImpl` reads the single scalar `errCd`/`errNm` directly. Both throw their feature-specific exception (`Cta4jRouteStatusException`/`Cta4jDetailedAlertsException`, both extending the shared `Cta4jAlertException`) for any code other than `OK`/not-found. + `find*` methods return an empty `List` (or `Optional.empty()`) for not-found; they never throw for missing resources. ## Annotations diff --git a/src/main/java/com/cta4j/bus/vehicle/VehiclesApi.java b/src/main/java/com/cta4j/bus/vehicle/VehiclesApi.java index c50b0a9c..83fccd40 100644 --- a/src/main/java/com/cta4j/bus/vehicle/VehiclesApi.java +++ b/src/main/java/com/cta4j/bus/vehicle/VehiclesApi.java @@ -17,27 +17,27 @@ public interface VehiclesApi { /// Retrieves vehicles by their IDs. /// - /// @param ids a [Collection] of vehicle IDs + /// @param vehicleIds a [Collection] of vehicle IDs /// @return a [List] of [Vehicle]s corresponding to the provided IDs, or an empty [List] if no vehicles are found - /// @throws NullPointerException if `ids` is `null`, or if any element of `ids` is `null` + /// @throws NullPointerException if `vehicleIds` is `null`, or if any element of `vehicleIds` is `null` /// @throws IllegalArgumentException if more than 10 vehicle IDs are provided /// @throws Cta4jBusException if the API returns an error response or the response cannot be parsed - List findByIds(Collection ids); + List findByIds(Collection vehicleIds); /// Retrieves a vehicle by its ID. /// - /// @param id the vehicle ID + /// @param vehicleId the vehicle ID /// @return an [Optional] containing the [Vehicle] if found, or an empty [Optional] if no vehicle is found for the /// given ID - /// @throws NullPointerException if `id` is `null` + /// @throws NullPointerException if `vehicleId` is `null` /// @throws Cta4jBusException if multiple vehicles are found for the given ID, or if the API returns an error /// response or the response cannot be parsed - default Optional findById(String id) { - Objects.requireNonNull(id); + default Optional findById(String vehicleId) { + Objects.requireNonNull(vehicleId); - List ids = List.of(id); + List vehicleIds = List.of(vehicleId); - List vehicles = this.findByIds(ids); + List vehicles = this.findByIds(vehicleIds); if (vehicles.isEmpty()) { return Optional.empty(); @@ -45,7 +45,7 @@ default Optional findById(String id) { if (vehicles.size() > 1) { String message = "Expected at most one vehicle for ID: %s, but found %d".formatted( - id, + vehicleId, vehicles.size() ); diff --git a/src/main/java/com/cta4j/bus/vehicle/internal/impl/VehiclesApiImpl.java b/src/main/java/com/cta4j/bus/vehicle/internal/impl/VehiclesApiImpl.java index 5d0ccf19..79cc4150 100644 --- a/src/main/java/com/cta4j/bus/vehicle/internal/impl/VehiclesApiImpl.java +++ b/src/main/java/com/cta4j/bus/vehicle/internal/impl/VehiclesApiImpl.java @@ -37,18 +37,18 @@ public VehiclesApiImpl(BusApiConfig config) { } @Override - public List findByIds(Collection ids) { - Objects.requireNonNull(ids); + public List findByIds(Collection vehicleIds) { + Objects.requireNonNull(vehicleIds); - ids = List.copyOf(ids); + vehicleIds = List.copyOf(vehicleIds); - if (ids.isEmpty()) { + if (vehicleIds.isEmpty()) { return List.of(); } - BusApiUtils.requireMaxIds(ids, "vehicle"); + BusApiUtils.requireMaxIds(vehicleIds, "vehicle"); - String idsString = String.join(",", ids); + String idsString = String.join(",", vehicleIds); String url = new URIBuilder() .setScheme(this.config.scheme()) From 10a98babfa60d6f1f0505b1f0256d009f1fb4c46 Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Sat, 1 Aug 2026 15:01:08 -0500 Subject: [PATCH 54/60] Remove UNKNOWN error codes in favor of null --- CHANGELOG.md | 9 +++++++++ .../exception/DetailedAlertsErrorCode.java | 14 ++++++-------- .../exception/RouteStatusErrorCode.java | 13 +++++-------- .../train/arrival/exception/ArrivalsErrorCode.java | 13 +++++-------- .../train/follow/exception/FollowErrorCode.java | 13 +++++-------- .../location/exception/LocationsErrorCode.java | 13 +++++-------- .../Cta4jDetailedAlertsExceptionTest.java | 4 ++-- .../exception/DetailedAlertsErrorCodeTest.java | 5 ++--- .../exception/Cta4jRouteStatusExceptionTest.java | 4 ++-- .../exception/RouteStatusErrorCodeTest.java | 5 ++--- .../cta4j/train/arrival/ArrivalsApiImplTest.java | 11 +++++------ .../arrival/exception/ArrivalsErrorCodeTest.java | 5 ++--- .../exception/Cta4jArrivalsExceptionTest.java | 4 ++-- .../com/cta4j/train/follow/FollowApiImplTest.java | 9 ++++----- .../follow/exception/Cta4jFollowExceptionTest.java | 4 ++-- .../follow/exception/FollowErrorCodeTest.java | 5 ++--- .../cta4j/train/location/LocationsApiImplTest.java | 9 ++++----- .../exception/Cta4jLocationsExceptionTest.java | 4 ++-- .../location/exception/LocationsErrorCodeTest.java | 5 ++--- 19 files changed, 68 insertions(+), 81 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 84e80a98..6e227d87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 for consistency with the rest of the query-builder naming. - `TrainLine.fromCode(String)` no longer throws `IllegalArgumentException` for an unrecognized code; it now returns `null` and logs a warning, matching the `@Nullable`-based degrade pattern used elsewhere in the SDK. +- `ArrivalsErrorCode.fromCode`/`FollowErrorCode.fromCode`/`LocationsErrorCode.fromCode`/ + `RouteStatusErrorCode.fromCode`/`DetailedAlertsErrorCode.fromCode` no longer fall back to an `UNKNOWN` + constant for an unrecognized code; they now return `null`, matching the same degrade pattern as + `TrainLine.fromCode`. +- Renamed `VehiclesApi.findByIds`'s parameter from `ids` to `vehicleIds`, and `findById`'s parameter from `id` + to `vehicleId`, for consistency with `StopsApi`/`PatternsApi`'s equivalent methods. ### Breaking Changes ⚠️ @@ -42,6 +48,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 the single `String mapId`/`stopId` component is now a `List mapIds`/`stopIds` component (max 4 IDs). - `ArrivalsApi.findByMapId(MapArrivalQuery)`/`findByStopId(StopArrivalQuery)` have been renamed to `findByMapIds(MapArrivalsQuery)`/`findByStopIds(StopArrivalsQuery)` to match the new query types. +- `ArrivalsErrorCode.UNKNOWN`/`FollowErrorCode.UNKNOWN`/`LocationsErrorCode.UNKNOWN`/ + `RouteStatusErrorCode.UNKNOWN`/`DetailedAlertsErrorCode.UNKNOWN` have been removed; code that referenced + these constants directly (e.g. `switch` statements, equality checks) must handle `null` instead. - `MapArrivalsQuery.Builder.maxResults`/`StopArrivalsQuery.Builder.maxResults` now take `int` instead of `Integer`; passing `null` no longer compiles. - `Arrival.line` is now `@Nullable`, a direct consequence of `TrainLine.fromCode` no longer throwing — code that diff --git a/src/main/java/com/cta4j/alert/detailedalert/exception/DetailedAlertsErrorCode.java b/src/main/java/com/cta4j/alert/detailedalert/exception/DetailedAlertsErrorCode.java index c44f6499..525e03fe 100644 --- a/src/main/java/com/cta4j/alert/detailedalert/exception/DetailedAlertsErrorCode.java +++ b/src/main/java/com/cta4j/alert/detailedalert/exception/DetailedAlertsErrorCode.java @@ -1,6 +1,7 @@ package com.cta4j.alert.detailedalert.exception; import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /// Represents the error codes returned by the CTA Detailed Alerts API. @NullMarked @@ -44,10 +45,7 @@ public enum DetailedAlertsErrorCode { INVALID_PARAMETER(500), /// Indicates that the server encountered an unexpected error that prevented it from fulfilling the request. - SERVER_ERROR(900), - - /// Indicates that an unknown error occurred that does not match any of the defined error codes. - UNKNOWN(-1); + SERVER_ERROR(900); private final int code; @@ -65,9 +63,9 @@ public int getCode() { /// Returns the `DetailedAlertsErrorCode` corresponding to the given integer code. /// /// @param code the integer code to look up - /// @return the corresponding `DetailedAlertsErrorCode`, or `UNKNOWN` if the code does not match any - /// defined error code - public static DetailedAlertsErrorCode fromCode(int code) { + /// @return the corresponding `DetailedAlertsErrorCode`, or `null` if the code does not match any defined error + /// code + public static @Nullable DetailedAlertsErrorCode fromCode(int code) { return switch (code) { case 0 -> OK; case 25 -> NO_ACTIVE_ALERTS; @@ -82,7 +80,7 @@ public static DetailedAlertsErrorCode fromCode(int code) { case 107 -> RECENTDAYS_BYSTARTDATE_CONFLICT; case 500 -> INVALID_PARAMETER; case 900 -> SERVER_ERROR; - default -> UNKNOWN; + default -> null; }; } } diff --git a/src/main/java/com/cta4j/alert/routestatus/exception/RouteStatusErrorCode.java b/src/main/java/com/cta4j/alert/routestatus/exception/RouteStatusErrorCode.java index f312ec05..2b970ea3 100644 --- a/src/main/java/com/cta4j/alert/routestatus/exception/RouteStatusErrorCode.java +++ b/src/main/java/com/cta4j/alert/routestatus/exception/RouteStatusErrorCode.java @@ -1,6 +1,7 @@ package com.cta4j.alert.routestatus.exception; import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /// Represents the error codes returned by the CTA Route Status API. @NullMarked @@ -33,10 +34,7 @@ public enum RouteStatusErrorCode { INVALID_PARAMETER(500), /// Indicates that the server encountered an unexpected error that prevented it from fulfilling the request. - SERVER_ERROR(900), - - /// Indicates that an unknown error occurred that does not match any of the defined error codes. - UNKNOWN(-1); + SERVER_ERROR(900); private final int code; @@ -54,9 +52,8 @@ public int getCode() { /// Returns the `RouteStatusErrorCode` corresponding to the given integer code. /// /// @param code the integer code to look up - /// @return the corresponding `RouteStatusErrorCode`, or `UNKNOWN` if the code does not match any defined error - /// code - public static RouteStatusErrorCode fromCode(int code) { + /// @return the corresponding `RouteStatusErrorCode`, or `null` if the code does not match any defined error code + public static @Nullable RouteStatusErrorCode fromCode(int code) { return switch (code) { case 0 -> OK; case 50 -> NO_RESULTS; @@ -67,7 +64,7 @@ public static RouteStatusErrorCode fromCode(int code) { case 104 -> STATIONID_TYPE_CONFLICT; case 500 -> INVALID_PARAMETER; case 900 -> SERVER_ERROR; - default -> UNKNOWN; + default -> null; }; } } diff --git a/src/main/java/com/cta4j/train/arrival/exception/ArrivalsErrorCode.java b/src/main/java/com/cta4j/train/arrival/exception/ArrivalsErrorCode.java index a7f345ab..0251b87d 100644 --- a/src/main/java/com/cta4j/train/arrival/exception/ArrivalsErrorCode.java +++ b/src/main/java/com/cta4j/train/arrival/exception/ArrivalsErrorCode.java @@ -1,6 +1,7 @@ package com.cta4j.train.arrival.exception; import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /// Represents the error codes returned by the CTA Arrivals API. @NullMarked @@ -52,10 +53,7 @@ public enum ArrivalsErrorCode { INVALID_PARAMETER(500), /// Indicates that the server encountered an unexpected error that prevented it from fulfilling the request. - SERVER_ERROR(900), - - /// Indicates that an unknown error occurred that does not match any of the defined error codes. - UNKNOWN(-1); + SERVER_ERROR(900); private final int code; @@ -73,9 +71,8 @@ public int getCode() { /// Returns the `ArrivalsErrorCode` corresponding to the given integer code. /// /// @param code the integer code to look up - /// @return the corresponding `ArrivalsErrorCode`, or `UNKNOWN` if the code does not match any defined - /// error code - public static ArrivalsErrorCode fromCode(int code) { + /// @return the corresponding `ArrivalsErrorCode`, or `null` if the code does not match any defined error code + public static @Nullable ArrivalsErrorCode fromCode(int code) { return switch (code) { case 0 -> OK; case 100 -> MISSING_PARAMETER; @@ -93,7 +90,7 @@ public static ArrivalsErrorCode fromCode(int code) { case 112 -> STPID_NOT_INTEGER; case 500 -> INVALID_PARAMETER; case 900 -> SERVER_ERROR; - default -> UNKNOWN; + default -> null; }; } } diff --git a/src/main/java/com/cta4j/train/follow/exception/FollowErrorCode.java b/src/main/java/com/cta4j/train/follow/exception/FollowErrorCode.java index d509fa56..16a5ebcd 100644 --- a/src/main/java/com/cta4j/train/follow/exception/FollowErrorCode.java +++ b/src/main/java/com/cta4j/train/follow/exception/FollowErrorCode.java @@ -1,6 +1,7 @@ package com.cta4j.train.follow.exception; import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /// Represents the error codes returned by the CTA Follow API. @NullMarked @@ -29,10 +30,7 @@ public enum FollowErrorCode { UNABLE_TO_DETERMINE_STOPS(502), /// Indicates that the specified train run exists, but none of its available predictions are for active stations. - UNABLE_TO_FIND_PREDICTIONS(503), - - /// Indicates that an unknown error occurred that does not match any of the defined error codes. - UNKNOWN(-1); + UNABLE_TO_FIND_PREDICTIONS(503); private final int code; @@ -50,9 +48,8 @@ public int getCode() { /// Returns the `FollowErrorCode` corresponding to the given integer code. /// /// @param code the integer code to look up - /// @return the corresponding `FollowErrorCode`, or `UNKNOWN` if the code does not match any defined - /// error code - public static FollowErrorCode fromCode(int code) { + /// @return the corresponding `FollowErrorCode`, or `null` if the code does not match any defined error code + public static @Nullable FollowErrorCode fromCode(int code) { return switch (code) { case 0 -> OK; case 100 -> MISSING_PARAMETER; @@ -62,7 +59,7 @@ public static FollowErrorCode fromCode(int code) { case 501 -> RUN_NOT_FOUND; case 502 -> UNABLE_TO_DETERMINE_STOPS; case 503 -> UNABLE_TO_FIND_PREDICTIONS; - default -> UNKNOWN; + default -> null; }; } } diff --git a/src/main/java/com/cta4j/train/location/exception/LocationsErrorCode.java b/src/main/java/com/cta4j/train/location/exception/LocationsErrorCode.java index 37bc493d..f699745e 100644 --- a/src/main/java/com/cta4j/train/location/exception/LocationsErrorCode.java +++ b/src/main/java/com/cta4j/train/location/exception/LocationsErrorCode.java @@ -1,6 +1,7 @@ package com.cta4j.train.location.exception; import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /// Represents the error codes returned by the CTA Location API. @NullMarked @@ -26,10 +27,7 @@ public enum LocationsErrorCode { /// Indicates that the query string contains a parameter that is not recognized by the API. The supported API /// parameters are `rt` and `key`. - INVALID_PARAMETER(500), - - /// Indicates that an unknown error occurred that does not match any of the defined error codes. - UNKNOWN(-1); + INVALID_PARAMETER(500); private final int code; @@ -47,9 +45,8 @@ public int getCode() { /// Returns the `LocationsErrorCode` corresponding to the given integer code. /// /// @param code the integer code to look up - /// @return the corresponding `LocationsErrorCode`, or `UNKNOWN` if the code does not match any defined - /// error code - public static LocationsErrorCode fromCode(int code) { + /// @return the corresponding `LocationsErrorCode`, or `null` if the code does not match any defined error code + public static @Nullable LocationsErrorCode fromCode(int code) { return switch (code) { case 0 -> OK; case 100 -> MISSING_PARAMETER; @@ -58,7 +55,7 @@ public static LocationsErrorCode fromCode(int code) { case 106 -> INVALID_ROUTE; case 107 -> TOO_MANY_ROUTES; case 500 -> INVALID_PARAMETER; - default -> UNKNOWN; + default -> null; }; } } diff --git a/src/test/java/com/cta4j/alert/detailedalert/exception/Cta4jDetailedAlertsExceptionTest.java b/src/test/java/com/cta4j/alert/detailedalert/exception/Cta4jDetailedAlertsExceptionTest.java index bce1a8df..195f7fb5 100644 --- a/src/test/java/com/cta4j/alert/detailedalert/exception/Cta4jDetailedAlertsExceptionTest.java +++ b/src/test/java/com/cta4j/alert/detailedalert/exception/Cta4jDetailedAlertsExceptionTest.java @@ -34,10 +34,10 @@ void constructor_setsMessageEndpointAndErrorCode() { } @Test - void constructor_setsUnknownErrorCode_whenRawErrorCodeIsUnrecognized() { + void constructor_setsNullErrorCode_whenRawErrorCodeIsUnrecognized() { Cta4jDetailedAlertsException exception = new Cta4jDetailedAlertsException("Something odd happened", 999); assertThat(exception.getRawErrorCode()).isEqualTo(999); - assertThat(exception.getErrorCode()).isEqualTo(DetailedAlertsErrorCode.UNKNOWN); + assertThat(exception.getErrorCode()).isNull(); } } diff --git a/src/test/java/com/cta4j/alert/detailedalert/exception/DetailedAlertsErrorCodeTest.java b/src/test/java/com/cta4j/alert/detailedalert/exception/DetailedAlertsErrorCodeTest.java index 02570a46..c1d4f45a 100644 --- a/src/test/java/com/cta4j/alert/detailedalert/exception/DetailedAlertsErrorCodeTest.java +++ b/src/test/java/com/cta4j/alert/detailedalert/exception/DetailedAlertsErrorCodeTest.java @@ -13,14 +13,13 @@ void fromCode_returnsCorrectValue_forEveryDefinedCode() { } @Test - void fromCode_returnsUnknown_whenCodeIsUnrecognized() { - assertThat(DetailedAlertsErrorCode.fromCode(12345)).isEqualTo(DetailedAlertsErrorCode.UNKNOWN); + void fromCode_returnsNull_whenCodeIsUnrecognized() { + assertThat(DetailedAlertsErrorCode.fromCode(12345)).isNull(); } @Test void getCode_returnsCode() { assertThat(DetailedAlertsErrorCode.NO_ACTIVE_ALERTS.getCode()).isEqualTo(25); assertThat(DetailedAlertsErrorCode.NO_ACTIVE_ALERTS_FOR_FILTER.getCode()).isEqualTo(50); - assertThat(DetailedAlertsErrorCode.UNKNOWN.getCode()).isEqualTo(-1); } } diff --git a/src/test/java/com/cta4j/alert/routestatus/exception/Cta4jRouteStatusExceptionTest.java b/src/test/java/com/cta4j/alert/routestatus/exception/Cta4jRouteStatusExceptionTest.java index 134698e5..4edfd425 100644 --- a/src/test/java/com/cta4j/alert/routestatus/exception/Cta4jRouteStatusExceptionTest.java +++ b/src/test/java/com/cta4j/alert/routestatus/exception/Cta4jRouteStatusExceptionTest.java @@ -32,10 +32,10 @@ void constructor_setsMessageEndpointAndErrorCode() { } @Test - void constructor_setsUnknownErrorCode_whenRawErrorCodeIsUnrecognized() { + void constructor_setsNullErrorCode_whenRawErrorCodeIsUnrecognized() { Cta4jRouteStatusException exception = new Cta4jRouteStatusException("Something odd happened", 999); assertThat(exception.getRawErrorCode()).isEqualTo(999); - assertThat(exception.getErrorCode()).isEqualTo(RouteStatusErrorCode.UNKNOWN); + assertThat(exception.getErrorCode()).isNull(); } } diff --git a/src/test/java/com/cta4j/alert/routestatus/exception/RouteStatusErrorCodeTest.java b/src/test/java/com/cta4j/alert/routestatus/exception/RouteStatusErrorCodeTest.java index 636a437e..be20ccbc 100644 --- a/src/test/java/com/cta4j/alert/routestatus/exception/RouteStatusErrorCodeTest.java +++ b/src/test/java/com/cta4j/alert/routestatus/exception/RouteStatusErrorCodeTest.java @@ -13,13 +13,12 @@ void fromCode_returnsCorrectValue_forEveryDefinedCode() { } @Test - void fromCode_returnsUnknown_whenCodeIsUnrecognized() { - assertThat(RouteStatusErrorCode.fromCode(12345)).isEqualTo(RouteStatusErrorCode.UNKNOWN); + void fromCode_returnsNull_whenCodeIsUnrecognized() { + assertThat(RouteStatusErrorCode.fromCode(12345)).isNull(); } @Test void getCode_returnsCode() { assertThat(RouteStatusErrorCode.NO_RESULTS.getCode()).isEqualTo(50); - assertThat(RouteStatusErrorCode.UNKNOWN.getCode()).isEqualTo(-1); } } diff --git a/src/test/java/com/cta4j/train/arrival/ArrivalsApiImplTest.java b/src/test/java/com/cta4j/train/arrival/ArrivalsApiImplTest.java index f24ffd00..4374e98a 100644 --- a/src/test/java/com/cta4j/train/arrival/ArrivalsApiImplTest.java +++ b/src/test/java/com/cta4j/train/arrival/ArrivalsApiImplTest.java @@ -1,7 +1,6 @@ package com.cta4j.train.arrival; import com.cta4j.TestFixtures; -import com.cta4j.train.arrival.exception.ArrivalsErrorCode; import com.cta4j.train.arrival.exception.Cta4jArrivalsException; import com.cta4j.train.arrival.internal.impl.ArrivalsApiImpl; import com.cta4j.train.arrival.query.MapArrivalsQuery; @@ -131,7 +130,7 @@ void findByMapIds_throwsCta4jArrivalsException_whenResponseContainsError() { .isInstanceOf(Cta4jArrivalsException.class) .hasMessage("Invalid API key") .satisfies(e -> assertThat(((Cta4jArrivalsException) e).getErrorCode()) - .isEqualTo(ArrivalsErrorCode.UNKNOWN)) + .isNull()) .satisfies(e -> assertThat(((Cta4jArrivalsException) e).getRawErrorCode()).isEqualTo(1)); } @@ -233,7 +232,7 @@ void findByStopIds_throwsCta4jArrivalsException_whenResponseContainsError() { .isInstanceOf(Cta4jArrivalsException.class) .hasMessage("Invalid API key") .satisfies(e -> assertThat(((Cta4jArrivalsException) e).getErrorCode()) - .isEqualTo(ArrivalsErrorCode.UNKNOWN)) + .isNull()) .satisfies(e -> assertThat(((Cta4jArrivalsException) e).getRawErrorCode()).isEqualTo(1)); } @@ -392,7 +391,7 @@ void findByMapIds_throwsCta4jArrivalsException_whenErrCdIsNegative() { .isInstanceOf(Cta4jArrivalsException.class) .hasMessage("Unknown error code") .satisfies(e -> assertThat(((Cta4jArrivalsException) e).getErrorCode()) - .isEqualTo(ArrivalsErrorCode.UNKNOWN)) + .isNull()) .satisfies(e -> assertThat(((Cta4jArrivalsException) e).getRawErrorCode()).isEqualTo(-1)); } @@ -410,7 +409,7 @@ void findByMapIds_throwsCta4jArrivalsException_withDefaultMessage_whenErrNmIsBla .isInstanceOf(Cta4jArrivalsException.class) .hasMessage("An unknown error occurred.") .satisfies(e -> assertThat(((Cta4jArrivalsException) e).getErrorCode()) - .isEqualTo(ArrivalsErrorCode.UNKNOWN)) + .isNull()) .satisfies(e -> assertThat(((Cta4jArrivalsException) e).getRawErrorCode()).isEqualTo(1)); } @@ -428,7 +427,7 @@ void findByMapIds_throwsCta4jArrivalsException_withDefaultMessage_whenErrNmIsAbs .isInstanceOf(Cta4jArrivalsException.class) .hasMessage("An unknown error occurred.") .satisfies(e -> assertThat(((Cta4jArrivalsException) e).getErrorCode()) - .isEqualTo(ArrivalsErrorCode.UNKNOWN)) + .isNull()) .satisfies(e -> assertThat(((Cta4jArrivalsException) e).getRawErrorCode()).isEqualTo(1)); } diff --git a/src/test/java/com/cta4j/train/arrival/exception/ArrivalsErrorCodeTest.java b/src/test/java/com/cta4j/train/arrival/exception/ArrivalsErrorCodeTest.java index 845b9b1e..a4d04d8b 100644 --- a/src/test/java/com/cta4j/train/arrival/exception/ArrivalsErrorCodeTest.java +++ b/src/test/java/com/cta4j/train/arrival/exception/ArrivalsErrorCodeTest.java @@ -13,13 +13,12 @@ void fromCode_returnsCorrectValue_forEveryDefinedCode() { } @Test - void fromCode_returnsUnknown_whenCodeIsUnrecognized() { - assertThat(ArrivalsErrorCode.fromCode(999)).isEqualTo(ArrivalsErrorCode.UNKNOWN); + void fromCode_returnsNull_whenCodeIsUnrecognized() { + assertThat(ArrivalsErrorCode.fromCode(999)).isNull(); } @Test void getCode_returnsCode() { assertThat(ArrivalsErrorCode.INVALID_API_KEY.getCode()).isEqualTo(101); - assertThat(ArrivalsErrorCode.UNKNOWN.getCode()).isEqualTo(-1); } } diff --git a/src/test/java/com/cta4j/train/arrival/exception/Cta4jArrivalsExceptionTest.java b/src/test/java/com/cta4j/train/arrival/exception/Cta4jArrivalsExceptionTest.java index 61e00e8e..8631e4be 100644 --- a/src/test/java/com/cta4j/train/arrival/exception/Cta4jArrivalsExceptionTest.java +++ b/src/test/java/com/cta4j/train/arrival/exception/Cta4jArrivalsExceptionTest.java @@ -31,10 +31,10 @@ void constructor_setsMessageEndpointAndErrorCode() { } @Test - void constructor_setsUnknownErrorCode_whenRawErrorCodeIsUnrecognized() { + void constructor_setsNullErrorCode_whenRawErrorCodeIsUnrecognized() { Cta4jArrivalsException exception = new Cta4jArrivalsException("Something odd happened", 999); assertThat(exception.getRawErrorCode()).isEqualTo(999); - assertThat(exception.getErrorCode()).isEqualTo(ArrivalsErrorCode.UNKNOWN); + assertThat(exception.getErrorCode()).isNull(); } } diff --git a/src/test/java/com/cta4j/train/follow/FollowApiImplTest.java b/src/test/java/com/cta4j/train/follow/FollowApiImplTest.java index ce8d55e8..0ce228a6 100644 --- a/src/test/java/com/cta4j/train/follow/FollowApiImplTest.java +++ b/src/test/java/com/cta4j/train/follow/FollowApiImplTest.java @@ -3,7 +3,6 @@ import com.cta4j.TestFixtures; import com.cta4j.train.common.internal.config.TrainApiConfig; import com.cta4j.train.follow.exception.Cta4jFollowException; -import com.cta4j.train.follow.exception.FollowErrorCode; import com.cta4j.train.follow.internal.impl.FollowApiImpl; import com.cta4j.train.follow.model.FollowTrain; import com.github.tomakehurst.wiremock.WireMockServer; @@ -98,7 +97,7 @@ void findByRun_throwsCta4jFollowException_whenResponseContainsError() { .isInstanceOf(Cta4jFollowException.class) .hasMessage("Invalid API key") .satisfies(e -> assertThat(((Cta4jFollowException) e).getErrorCode()) - .isEqualTo(FollowErrorCode.UNKNOWN)) + .isNull()) .satisfies(e -> assertThat(((Cta4jFollowException) e).getRawErrorCode()).isEqualTo(1)); } @@ -158,7 +157,7 @@ void findByRun_throwsCta4jFollowException_whenErrCdIsNegative() { .isInstanceOf(Cta4jFollowException.class) .hasMessage("Unknown error code") .satisfies(e -> assertThat(((Cta4jFollowException) e).getErrorCode()) - .isEqualTo(FollowErrorCode.UNKNOWN)) + .isNull()) .satisfies(e -> assertThat(((Cta4jFollowException) e).getRawErrorCode()).isEqualTo(-1)); } @@ -174,7 +173,7 @@ void findByRun_throwsCta4jFollowException_withDefaultMessage_whenErrNmIsBlank() .isInstanceOf(Cta4jFollowException.class) .hasMessage("An unknown error occurred.") .satisfies(e -> assertThat(((Cta4jFollowException) e).getErrorCode()) - .isEqualTo(FollowErrorCode.UNKNOWN)) + .isNull()) .satisfies(e -> assertThat(((Cta4jFollowException) e).getRawErrorCode()).isEqualTo(1)); } @@ -190,7 +189,7 @@ void findByRun_throwsCta4jFollowException_withDefaultMessage_whenErrNmIsAbsent() .isInstanceOf(Cta4jFollowException.class) .hasMessage("An unknown error occurred.") .satisfies(e -> assertThat(((Cta4jFollowException) e).getErrorCode()) - .isEqualTo(FollowErrorCode.UNKNOWN)) + .isNull()) .satisfies(e -> assertThat(((Cta4jFollowException) e).getRawErrorCode()).isEqualTo(1)); } diff --git a/src/test/java/com/cta4j/train/follow/exception/Cta4jFollowExceptionTest.java b/src/test/java/com/cta4j/train/follow/exception/Cta4jFollowExceptionTest.java index e5817b53..ba4a3101 100644 --- a/src/test/java/com/cta4j/train/follow/exception/Cta4jFollowExceptionTest.java +++ b/src/test/java/com/cta4j/train/follow/exception/Cta4jFollowExceptionTest.java @@ -31,10 +31,10 @@ void constructor_setsMessageEndpointAndErrorCode() { } @Test - void constructor_setsUnknownErrorCode_whenRawErrorCodeIsUnrecognized() { + void constructor_setsNullErrorCode_whenRawErrorCodeIsUnrecognized() { Cta4jFollowException exception = new Cta4jFollowException("Something odd happened", 999); assertThat(exception.getRawErrorCode()).isEqualTo(999); - assertThat(exception.getErrorCode()).isEqualTo(FollowErrorCode.UNKNOWN); + assertThat(exception.getErrorCode()).isNull(); } } diff --git a/src/test/java/com/cta4j/train/follow/exception/FollowErrorCodeTest.java b/src/test/java/com/cta4j/train/follow/exception/FollowErrorCodeTest.java index 30c7439a..99559d4d 100644 --- a/src/test/java/com/cta4j/train/follow/exception/FollowErrorCodeTest.java +++ b/src/test/java/com/cta4j/train/follow/exception/FollowErrorCodeTest.java @@ -13,13 +13,12 @@ void fromCode_returnsCorrectValue_forEveryDefinedCode() { } @Test - void fromCode_returnsUnknown_whenCodeIsUnrecognized() { - assertThat(FollowErrorCode.fromCode(999)).isEqualTo(FollowErrorCode.UNKNOWN); + void fromCode_returnsNull_whenCodeIsUnrecognized() { + assertThat(FollowErrorCode.fromCode(999)).isNull(); } @Test void getCode_returnsCode() { assertThat(FollowErrorCode.RUN_NOT_FOUND.getCode()).isEqualTo(501); - assertThat(FollowErrorCode.UNKNOWN.getCode()).isEqualTo(-1); } } diff --git a/src/test/java/com/cta4j/train/location/LocationsApiImplTest.java b/src/test/java/com/cta4j/train/location/LocationsApiImplTest.java index cfaca7b6..b6ee1701 100644 --- a/src/test/java/com/cta4j/train/location/LocationsApiImplTest.java +++ b/src/test/java/com/cta4j/train/location/LocationsApiImplTest.java @@ -4,7 +4,6 @@ import com.cta4j.train.common.internal.config.TrainApiConfig; import com.cta4j.train.common.model.TrainLine; import com.cta4j.train.location.exception.Cta4jLocationsException; -import com.cta4j.train.location.exception.LocationsErrorCode; import com.cta4j.train.location.internal.impl.LocationsApiImpl; import com.cta4j.train.location.model.TrainLocations; import com.github.tomakehurst.wiremock.WireMockServer; @@ -91,7 +90,7 @@ void findByLines_throwsCta4jLocationsException_whenResponseContainsError() { .isInstanceOf(Cta4jLocationsException.class) .hasMessage("Invalid API key") .satisfies(e -> assertThat(((Cta4jLocationsException) e).getErrorCode()) - .isEqualTo(LocationsErrorCode.UNKNOWN)) + .isNull()) .satisfies(e -> assertThat(((Cta4jLocationsException) e).getRawErrorCode()).isEqualTo(1)); } @@ -178,7 +177,7 @@ void findByLines_throwsCta4jLocationsException_whenErrCdIsNegative() { .isInstanceOf(Cta4jLocationsException.class) .hasMessage("Unknown error code") .satisfies(e -> assertThat(((Cta4jLocationsException) e).getErrorCode()) - .isEqualTo(LocationsErrorCode.UNKNOWN)) + .isNull()) .satisfies(e -> assertThat(((Cta4jLocationsException) e).getRawErrorCode()).isEqualTo(-1)); } @@ -194,7 +193,7 @@ void findByLines_throwsCta4jLocationsException_withDefaultMessage_whenErrNmIsBla .isInstanceOf(Cta4jLocationsException.class) .hasMessage("An unknown error occurred.") .satisfies(e -> assertThat(((Cta4jLocationsException) e).getErrorCode()) - .isEqualTo(LocationsErrorCode.UNKNOWN)) + .isNull()) .satisfies(e -> assertThat(((Cta4jLocationsException) e).getRawErrorCode()).isEqualTo(1)); } @@ -210,7 +209,7 @@ void findByLines_throwsCta4jLocationsException_withDefaultMessage_whenErrNmIsAbs .isInstanceOf(Cta4jLocationsException.class) .hasMessage("An unknown error occurred.") .satisfies(e -> assertThat(((Cta4jLocationsException) e).getErrorCode()) - .isEqualTo(LocationsErrorCode.UNKNOWN)) + .isNull()) .satisfies(e -> assertThat(((Cta4jLocationsException) e).getRawErrorCode()).isEqualTo(1)); } diff --git a/src/test/java/com/cta4j/train/location/exception/Cta4jLocationsExceptionTest.java b/src/test/java/com/cta4j/train/location/exception/Cta4jLocationsExceptionTest.java index 2f64d52f..d59ef868 100644 --- a/src/test/java/com/cta4j/train/location/exception/Cta4jLocationsExceptionTest.java +++ b/src/test/java/com/cta4j/train/location/exception/Cta4jLocationsExceptionTest.java @@ -31,10 +31,10 @@ void constructor_setsMessageEndpointAndErrorCode() { } @Test - void constructor_setsUnknownErrorCode_whenRawErrorCodeIsUnrecognized() { + void constructor_setsNullErrorCode_whenRawErrorCodeIsUnrecognized() { Cta4jLocationsException exception = new Cta4jLocationsException("Something odd happened", 999); assertThat(exception.getRawErrorCode()).isEqualTo(999); - assertThat(exception.getErrorCode()).isEqualTo(LocationsErrorCode.UNKNOWN); + assertThat(exception.getErrorCode()).isNull(); } } diff --git a/src/test/java/com/cta4j/train/location/exception/LocationsErrorCodeTest.java b/src/test/java/com/cta4j/train/location/exception/LocationsErrorCodeTest.java index e4ce1ebf..b08db97b 100644 --- a/src/test/java/com/cta4j/train/location/exception/LocationsErrorCodeTest.java +++ b/src/test/java/com/cta4j/train/location/exception/LocationsErrorCodeTest.java @@ -13,13 +13,12 @@ void fromCode_returnsCorrectValue_forEveryDefinedCode() { } @Test - void fromCode_returnsUnknown_whenCodeIsUnrecognized() { - assertThat(LocationsErrorCode.fromCode(999)).isEqualTo(LocationsErrorCode.UNKNOWN); + void fromCode_returnsNull_whenCodeIsUnrecognized() { + assertThat(LocationsErrorCode.fromCode(999)).isNull(); } @Test void getCode_returnsCode() { assertThat(LocationsErrorCode.INVALID_ROUTE.getCode()).isEqualTo(106); - assertThat(LocationsErrorCode.UNKNOWN.getCode()).isEqualTo(-1); } } From 5ae1ed986dd93eac75ca042525297a49438313b9 Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Sat, 1 Aug 2026 15:19:59 -0500 Subject: [PATCH 55/60] Import ordering fixes --- src/main/java/com/cta4j/alert/routestatus/RouteStatusApi.java | 2 +- .../alert/routestatus/internal/impl/RouteStatusApiImpl.java | 2 +- .../com/cta4j/bus/detour/internal/impl/DetoursApiImpl.java | 2 +- .../cta4j/bus/direction/internal/impl/DirectionsApiImpl.java | 2 +- .../com/cta4j/bus/locale/internal/impl/LocalesApiImpl.java | 2 +- .../com/cta4j/bus/pattern/internal/impl/PatternsApiImpl.java | 2 +- .../bus/prediction/internal/impl/PredictionsApiImpl.java | 2 +- .../java/com/cta4j/bus/route/internal/impl/RoutesApiImpl.java | 2 +- .../java/com/cta4j/bus/stop/internal/impl/StopsApiImpl.java | 2 +- .../com/cta4j/bus/vehicle/internal/impl/VehiclesApiImpl.java | 2 +- .../com/cta4j/alert/routestatus/RouteStatusApiImplTest.java | 4 ++-- .../com/cta4j/alert/routestatus/RouteStatusMapperTest.java | 2 +- 12 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/cta4j/alert/routestatus/RouteStatusApi.java b/src/main/java/com/cta4j/alert/routestatus/RouteStatusApi.java index fee89f7f..09f68471 100644 --- a/src/main/java/com/cta4j/alert/routestatus/RouteStatusApi.java +++ b/src/main/java/com/cta4j/alert/routestatus/RouteStatusApi.java @@ -1,9 +1,9 @@ package com.cta4j.alert.routestatus; import com.cta4j.alert.common.model.AlertTrainLine; +import com.cta4j.alert.common.model.ServiceType; import com.cta4j.alert.routestatus.exception.Cta4jRouteStatusException; import com.cta4j.alert.routestatus.model.RouteStatus; -import com.cta4j.alert.common.model.ServiceType; import org.jspecify.annotations.NullMarked; import java.util.Collection; diff --git a/src/main/java/com/cta4j/alert/routestatus/internal/impl/RouteStatusApiImpl.java b/src/main/java/com/cta4j/alert/routestatus/internal/impl/RouteStatusApiImpl.java index c51e6a00..90b276d6 100644 --- a/src/main/java/com/cta4j/alert/routestatus/internal/impl/RouteStatusApiImpl.java +++ b/src/main/java/com/cta4j/alert/routestatus/internal/impl/RouteStatusApiImpl.java @@ -3,6 +3,7 @@ import com.cta4j.alert.common.internal.config.AlertApiConfig; import com.cta4j.alert.common.internal.util.AlertApiConstants; import com.cta4j.alert.common.model.AlertTrainLine; +import com.cta4j.alert.common.model.ServiceType; import com.cta4j.alert.routestatus.RouteStatusApi; import com.cta4j.alert.routestatus.exception.Cta4jRouteStatusException; import com.cta4j.alert.routestatus.exception.RouteStatusErrorCode; @@ -11,7 +12,6 @@ import com.cta4j.alert.routestatus.internal.wire.CtaRouteStatusResponse; import com.cta4j.alert.routestatus.internal.wire.CtaRoutes; import com.cta4j.alert.routestatus.model.RouteStatus; -import com.cta4j.alert.common.model.ServiceType; import org.apache.hc.client5.http.fluent.Request; import org.apache.hc.core5.net.URIBuilder; import org.jetbrains.annotations.ApiStatus; diff --git a/src/main/java/com/cta4j/bus/detour/internal/impl/DetoursApiImpl.java b/src/main/java/com/cta4j/bus/detour/internal/impl/DetoursApiImpl.java index 9f20bd4c..67c6f4e1 100644 --- a/src/main/java/com/cta4j/bus/detour/internal/impl/DetoursApiImpl.java +++ b/src/main/java/com/cta4j/bus/detour/internal/impl/DetoursApiImpl.java @@ -2,8 +2,8 @@ import com.cta4j.bus.common.exception.Cta4jBusException; import com.cta4j.bus.common.internal.config.BusApiConfig; -import com.cta4j.bus.common.internal.util.BusApiUtils; import com.cta4j.bus.common.internal.util.BusApiConstants; +import com.cta4j.bus.common.internal.util.BusApiUtils; import com.cta4j.bus.common.internal.wire.CtaResponse; import com.cta4j.bus.detour.DetoursApi; import com.cta4j.bus.detour.internal.mapper.DetourMapper; diff --git a/src/main/java/com/cta4j/bus/direction/internal/impl/DirectionsApiImpl.java b/src/main/java/com/cta4j/bus/direction/internal/impl/DirectionsApiImpl.java index 053902fb..df0601ab 100644 --- a/src/main/java/com/cta4j/bus/direction/internal/impl/DirectionsApiImpl.java +++ b/src/main/java/com/cta4j/bus/direction/internal/impl/DirectionsApiImpl.java @@ -2,8 +2,8 @@ import com.cta4j.bus.common.exception.Cta4jBusException; import com.cta4j.bus.common.internal.config.BusApiConfig; -import com.cta4j.bus.common.internal.util.BusApiUtils; import com.cta4j.bus.common.internal.util.BusApiConstants; +import com.cta4j.bus.common.internal.util.BusApiUtils; import com.cta4j.bus.common.internal.wire.CtaResponse; import com.cta4j.bus.direction.DirectionsApi; import com.cta4j.bus.direction.internal.wire.CtaDirection; diff --git a/src/main/java/com/cta4j/bus/locale/internal/impl/LocalesApiImpl.java b/src/main/java/com/cta4j/bus/locale/internal/impl/LocalesApiImpl.java index faece1e9..f842c748 100644 --- a/src/main/java/com/cta4j/bus/locale/internal/impl/LocalesApiImpl.java +++ b/src/main/java/com/cta4j/bus/locale/internal/impl/LocalesApiImpl.java @@ -3,13 +3,13 @@ import com.cta4j.bus.common.exception.Cta4jBusException; import com.cta4j.bus.common.internal.config.BusApiConfig; import com.cta4j.bus.common.internal.util.BusApiConstants; +import com.cta4j.bus.common.internal.util.BusApiUtils; import com.cta4j.bus.common.internal.wire.CtaResponse; import com.cta4j.bus.locale.LocalesApi; import com.cta4j.bus.locale.internal.mapper.SupportedLocaleMapper; import com.cta4j.bus.locale.internal.wire.CtaLocale; import com.cta4j.bus.locale.internal.wire.CtaLocaleBustimeResponse; import com.cta4j.bus.locale.internal.wire.CtaLocaleError; -import com.cta4j.bus.common.internal.util.BusApiUtils; import com.cta4j.bus.locale.model.SupportedLocale; import org.apache.hc.client5.http.fluent.Request; import org.apache.hc.core5.net.URIBuilder; diff --git a/src/main/java/com/cta4j/bus/pattern/internal/impl/PatternsApiImpl.java b/src/main/java/com/cta4j/bus/pattern/internal/impl/PatternsApiImpl.java index f428f8da..872c4479 100644 --- a/src/main/java/com/cta4j/bus/pattern/internal/impl/PatternsApiImpl.java +++ b/src/main/java/com/cta4j/bus/pattern/internal/impl/PatternsApiImpl.java @@ -2,8 +2,8 @@ import com.cta4j.bus.common.exception.Cta4jBusException; import com.cta4j.bus.common.internal.config.BusApiConfig; -import com.cta4j.bus.common.internal.util.BusApiUtils; import com.cta4j.bus.common.internal.util.BusApiConstants; +import com.cta4j.bus.common.internal.util.BusApiUtils; import com.cta4j.bus.common.internal.wire.CtaResponse; import com.cta4j.bus.pattern.PatternsApi; import com.cta4j.bus.pattern.internal.mapper.RoutePatternMapper; diff --git a/src/main/java/com/cta4j/bus/prediction/internal/impl/PredictionsApiImpl.java b/src/main/java/com/cta4j/bus/prediction/internal/impl/PredictionsApiImpl.java index f8845c0c..fabdea3e 100644 --- a/src/main/java/com/cta4j/bus/prediction/internal/impl/PredictionsApiImpl.java +++ b/src/main/java/com/cta4j/bus/prediction/internal/impl/PredictionsApiImpl.java @@ -2,8 +2,8 @@ import com.cta4j.bus.common.exception.Cta4jBusException; import com.cta4j.bus.common.internal.config.BusApiConfig; -import com.cta4j.bus.common.internal.util.BusApiUtils; import com.cta4j.bus.common.internal.util.BusApiConstants; +import com.cta4j.bus.common.internal.util.BusApiUtils; import com.cta4j.bus.common.internal.wire.CtaResponse; import com.cta4j.bus.prediction.PredictionsApi; import com.cta4j.bus.prediction.internal.mapper.PredictionMapper; diff --git a/src/main/java/com/cta4j/bus/route/internal/impl/RoutesApiImpl.java b/src/main/java/com/cta4j/bus/route/internal/impl/RoutesApiImpl.java index 4052c7b3..be28d0fc 100644 --- a/src/main/java/com/cta4j/bus/route/internal/impl/RoutesApiImpl.java +++ b/src/main/java/com/cta4j/bus/route/internal/impl/RoutesApiImpl.java @@ -2,8 +2,8 @@ import com.cta4j.bus.common.exception.Cta4jBusException; import com.cta4j.bus.common.internal.config.BusApiConfig; -import com.cta4j.bus.common.internal.util.BusApiUtils; import com.cta4j.bus.common.internal.util.BusApiConstants; +import com.cta4j.bus.common.internal.util.BusApiUtils; import com.cta4j.bus.common.internal.wire.CtaResponse; import com.cta4j.bus.route.RoutesApi; import com.cta4j.bus.route.internal.mapper.RouteMapper; diff --git a/src/main/java/com/cta4j/bus/stop/internal/impl/StopsApiImpl.java b/src/main/java/com/cta4j/bus/stop/internal/impl/StopsApiImpl.java index c2e56cf6..379d65cd 100644 --- a/src/main/java/com/cta4j/bus/stop/internal/impl/StopsApiImpl.java +++ b/src/main/java/com/cta4j/bus/stop/internal/impl/StopsApiImpl.java @@ -2,8 +2,8 @@ import com.cta4j.bus.common.exception.Cta4jBusException; import com.cta4j.bus.common.internal.config.BusApiConfig; -import com.cta4j.bus.common.internal.util.BusApiUtils; import com.cta4j.bus.common.internal.util.BusApiConstants; +import com.cta4j.bus.common.internal.util.BusApiUtils; import com.cta4j.bus.common.internal.wire.CtaResponse; import com.cta4j.bus.stop.StopsApi; import com.cta4j.bus.stop.internal.mapper.StopMapper; diff --git a/src/main/java/com/cta4j/bus/vehicle/internal/impl/VehiclesApiImpl.java b/src/main/java/com/cta4j/bus/vehicle/internal/impl/VehiclesApiImpl.java index 79cc4150..0b67b4e7 100644 --- a/src/main/java/com/cta4j/bus/vehicle/internal/impl/VehiclesApiImpl.java +++ b/src/main/java/com/cta4j/bus/vehicle/internal/impl/VehiclesApiImpl.java @@ -2,8 +2,8 @@ import com.cta4j.bus.common.exception.Cta4jBusException; import com.cta4j.bus.common.internal.config.BusApiConfig; -import com.cta4j.bus.common.internal.util.BusApiUtils; import com.cta4j.bus.common.internal.util.BusApiConstants; +import com.cta4j.bus.common.internal.util.BusApiUtils; import com.cta4j.bus.common.internal.wire.CtaResponse; import com.cta4j.bus.vehicle.VehiclesApi; import com.cta4j.bus.vehicle.internal.mapper.VehicleMapper; diff --git a/src/test/java/com/cta4j/alert/routestatus/RouteStatusApiImplTest.java b/src/test/java/com/cta4j/alert/routestatus/RouteStatusApiImplTest.java index 6d725f68..cd3df713 100644 --- a/src/test/java/com/cta4j/alert/routestatus/RouteStatusApiImplTest.java +++ b/src/test/java/com/cta4j/alert/routestatus/RouteStatusApiImplTest.java @@ -3,12 +3,12 @@ import com.cta4j.TestFixtures; import com.cta4j.alert.common.internal.config.AlertApiConfig; import com.cta4j.alert.common.internal.util.AlertApiConstants; +import com.cta4j.alert.common.model.AlertTrainLine; +import com.cta4j.alert.common.model.ServiceType; import com.cta4j.alert.routestatus.exception.Cta4jRouteStatusException; import com.cta4j.alert.routestatus.exception.RouteStatusErrorCode; import com.cta4j.alert.routestatus.internal.impl.RouteStatusApiImpl; import com.cta4j.alert.routestatus.model.RouteStatus; -import com.cta4j.alert.common.model.ServiceType; -import com.cta4j.alert.common.model.AlertTrainLine; import com.github.tomakehurst.wiremock.WireMockServer; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; diff --git a/src/test/java/com/cta4j/alert/routestatus/RouteStatusMapperTest.java b/src/test/java/com/cta4j/alert/routestatus/RouteStatusMapperTest.java index 56bc597a..6257dc83 100644 --- a/src/test/java/com/cta4j/alert/routestatus/RouteStatusMapperTest.java +++ b/src/test/java/com/cta4j/alert/routestatus/RouteStatusMapperTest.java @@ -1,8 +1,8 @@ package com.cta4j.alert.routestatus; +import com.cta4j.alert.common.internal.wire.CtaCdata; import com.cta4j.alert.routestatus.internal.mapper.RouteStatusMapper; import com.cta4j.alert.routestatus.internal.wire.CtaRouteInfo; -import com.cta4j.alert.common.internal.wire.CtaCdata; import com.cta4j.alert.routestatus.model.RouteStatus; import org.junit.jupiter.api.Test; From 2215d0be1131f3c015354041688054f26b8d3c39 Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Sat, 1 Aug 2026 15:51:03 -0500 Subject: [PATCH 56/60] Pre-merge review fixes --- CHANGELOG.md | 24 +++++++++++-------- .../query/VehiclePredictionsQuery.java | 2 +- .../alert/detailedalert/bad_error_code.json | 2 +- .../detailedalert/empty_alert_array.json | 2 +- .../detailedalert/error_message_absent.json | 2 +- .../detailedalert/error_message_blank.json | 2 +- .../alert/detailedalert/fatal_error.json | 2 +- .../alert/detailedalert/list_success.json | 2 +- .../alert/detailedalert/no_active_alerts.json | 2 +- .../no_active_alerts_for_filter.json | 2 +- .../alert/detailedalert/ok_no_alerts.json | 2 +- .../alert/routestatus/bad_error_code.json | 2 +- .../routestatus/blank_error_message.json | 2 +- .../alert/routestatus/bus_success.json | 2 +- .../routestatus/distinct_error_codes.json | 2 +- .../routestatus/empty_route_info_array.json | 2 +- .../routestatus/error_code_empty_array.json | 2 +- .../error_message_empty_array.json | 2 +- .../alert/routestatus/error_no_message.json | 2 +- .../error_null_message_element.json | 2 +- .../alert/routestatus/invalid_type_error.json | 2 +- .../alert/routestatus/list_success.json | 2 +- .../alert/routestatus/no_data_no_error.json | 2 +- .../alert/routestatus/no_results.json | 2 +- .../alert/routestatus/rail_success.json | 2 +- .../bus/detour/empty_dtrs_array.json | 2 +- .../bus/direction/empty_directions_array.json | 2 +- .../bus/locale/empty_locale_array.json | 2 +- .../bus/pattern/empty_ptr_array.json | 2 +- .../bus/prediction/empty_prd_array.json | 2 +- .../bus/route/empty_routes_array.json | 2 +- .../resources/bus/time/empty-error-array.json | 2 +- .../bus/vehicle/empty_vehicle_array.json | 2 +- 33 files changed, 46 insertions(+), 42 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e227d87..cb393f8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,19 +24,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `ArrivalsApi.findByMapIds`/`findByStopIds` — multi-value lookups accepting up to 4 map/stop IDs per request (`MapArrivalsQuery`/`StopArrivalsQuery`, plus `Collection` convenience overloads), alongside the existing single-ID `findByMapId`/`findByStopId`. +- `PredictionsApi.findByStopIds`/`findByVehicleIds` — convenience overloads accepting a bare + `Collection` without requiring a full `StopPredictionsQuery`/`VehiclePredictionsQuery` to be + constructed, matching `ArrivalsApi`'s equivalent overloads. ### Changed -- Renamed `StopsPredictionsQuery`/`VehiclesPredictionsQuery` to `StopPredictionsQuery`/`VehiclePredictionsQuery` - for consistency with the rest of the query-builder naming. - `TrainLine.fromCode(String)` no longer throws `IllegalArgumentException` for an unrecognized code; it now returns `null` and logs a warning, matching the `@Nullable`-based degrade pattern used elsewhere in the SDK. -- `ArrivalsErrorCode.fromCode`/`FollowErrorCode.fromCode`/`LocationsErrorCode.fromCode`/ - `RouteStatusErrorCode.fromCode`/`DetailedAlertsErrorCode.fromCode` no longer fall back to an `UNKNOWN` - constant for an unrecognized code; they now return `null`, matching the same degrade pattern as +- `ArrivalsErrorCode.fromCode`/`FollowErrorCode.fromCode`/`LocationsErrorCode.fromCode` no longer fall back to + an `UNKNOWN` constant for an unrecognized code; they now return `null`, matching the same degrade pattern as `TrainLine.fromCode`. - Renamed `VehiclesApi.findByIds`'s parameter from `ids` to `vehicleIds`, and `findById`'s parameter from `id` to `vehicleId`, for consistency with `StopsApi`/`PatternsApi`'s equivalent methods. +- Bumped `tools.jackson.core:jackson-databind` from **3.2.0** → **3.2.1** +- Bumped `org.apache.httpcomponents.client5:httpclient5-fluent` from **5.6.1** → **5.6.2** ### Breaking Changes ⚠️ @@ -44,17 +46,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 `VehiclePredictionsQuery`, `MapArrivalsQuery`, `StopArrivalsQuery`, `AlertsQuery`, `BusRouteAlertsQuery`, `LineAlertsQuery`, `StationAlertsQuery`). Construct instances via the static `builder(...)` factory method only. +- Renamed `StopsPredictionsQuery`/`VehiclesPredictionsQuery` to `StopPredictionsQuery`/`VehiclePredictionsQuery` + for consistency with the rest of the query-builder naming. - `MapArrivalQuery`/`StopArrivalQuery` have been renamed and reshaped to `MapArrivalsQuery`/`StopArrivalsQuery`: the single `String mapId`/`stopId` component is now a `List mapIds`/`stopIds` component (max 4 IDs). - `ArrivalsApi.findByMapId(MapArrivalQuery)`/`findByStopId(StopArrivalQuery)` have been renamed to `findByMapIds(MapArrivalsQuery)`/`findByStopIds(StopArrivalsQuery)` to match the new query types. -- `ArrivalsErrorCode.UNKNOWN`/`FollowErrorCode.UNKNOWN`/`LocationsErrorCode.UNKNOWN`/ - `RouteStatusErrorCode.UNKNOWN`/`DetailedAlertsErrorCode.UNKNOWN` have been removed; code that referenced - these constants directly (e.g. `switch` statements, equality checks) must handle `null` instead. +- `ArrivalsErrorCode.UNKNOWN`/`FollowErrorCode.UNKNOWN`/`LocationsErrorCode.UNKNOWN` have been removed; code + that referenced these constants directly (e.g. `switch` statements, equality checks) must handle `null` + instead. - `MapArrivalsQuery.Builder.maxResults`/`StopArrivalsQuery.Builder.maxResults` now take `int` instead of `Integer`; passing `null` no longer compiles. -- `Arrival.line` is now `@Nullable`, a direct consequence of `TrainLine.fromCode` no longer throwing — code that - assumed `line()` was always non-null must add a null check. +- `Arrival.line`/`TrainLocations.line` are now `@Nullable`, a direct consequence of `TrainLine.fromCode` no + longer throwing — code that assumed either was always non-null must add a null check. - `LocationsApi.findAll()` has been renamed to `LocationsApi.list()`. ## [6.0.0] - 2026-07-05 diff --git a/src/main/java/com/cta4j/bus/prediction/query/VehiclePredictionsQuery.java b/src/main/java/com/cta4j/bus/prediction/query/VehiclePredictionsQuery.java index 7fc22995..560b3666 100644 --- a/src/main/java/com/cta4j/bus/prediction/query/VehiclePredictionsQuery.java +++ b/src/main/java/com/cta4j/bus/prediction/query/VehiclePredictionsQuery.java @@ -60,7 +60,7 @@ private Builder(Collection vehicleIds) { /// Sets the maximum number of predictions to return. /// - /// @param maxResults the maximum number of predictions to return + /// @param maxResults the maximum number of predictions /// @return this `Builder` instance /// @throws IllegalArgumentException if `maxResults` is not positive public Builder maxResults(int maxResults) { diff --git a/src/test/resources/alert/detailedalert/bad_error_code.json b/src/test/resources/alert/detailedalert/bad_error_code.json index a682cce1..24b39b75 100644 --- a/src/test/resources/alert/detailedalert/bad_error_code.json +++ b/src/test/resources/alert/detailedalert/bad_error_code.json @@ -4,4 +4,4 @@ "ErrorCode": "abc", "ErrorMessage": "Something odd happened" } -} \ No newline at end of file +} diff --git a/src/test/resources/alert/detailedalert/empty_alert_array.json b/src/test/resources/alert/detailedalert/empty_alert_array.json index 10689d04..841eef0f 100644 --- a/src/test/resources/alert/detailedalert/empty_alert_array.json +++ b/src/test/resources/alert/detailedalert/empty_alert_array.json @@ -5,4 +5,4 @@ "ErrorMessage": null, "Alert": [] } -} \ No newline at end of file +} diff --git a/src/test/resources/alert/detailedalert/error_message_absent.json b/src/test/resources/alert/detailedalert/error_message_absent.json index 9ecb9b88..92365167 100644 --- a/src/test/resources/alert/detailedalert/error_message_absent.json +++ b/src/test/resources/alert/detailedalert/error_message_absent.json @@ -3,4 +3,4 @@ "TimeStamp": "2026-07-28T12:00:00", "ErrorCode": "500" } -} \ No newline at end of file +} diff --git a/src/test/resources/alert/detailedalert/error_message_blank.json b/src/test/resources/alert/detailedalert/error_message_blank.json index bf2f6944..d7da5ead 100644 --- a/src/test/resources/alert/detailedalert/error_message_blank.json +++ b/src/test/resources/alert/detailedalert/error_message_blank.json @@ -4,4 +4,4 @@ "ErrorCode": "900", "ErrorMessage": "" } -} \ No newline at end of file +} diff --git a/src/test/resources/alert/detailedalert/fatal_error.json b/src/test/resources/alert/detailedalert/fatal_error.json index 96be25a9..b19daa12 100644 --- a/src/test/resources/alert/detailedalert/fatal_error.json +++ b/src/test/resources/alert/detailedalert/fatal_error.json @@ -4,4 +4,4 @@ "ErrorCode": "100", "ErrorMessage": "Invalid option for parameter 'activeonly': Valid options are 'true', 'false'" } -} \ No newline at end of file +} diff --git a/src/test/resources/alert/detailedalert/list_success.json b/src/test/resources/alert/detailedalert/list_success.json index 88f89f43..408e9d24 100644 --- a/src/test/resources/alert/detailedalert/list_success.json +++ b/src/test/resources/alert/detailedalert/list_success.json @@ -101,4 +101,4 @@ } ] } -} \ No newline at end of file +} diff --git a/src/test/resources/alert/detailedalert/no_active_alerts.json b/src/test/resources/alert/detailedalert/no_active_alerts.json index 960a3f70..69deaf3a 100644 --- a/src/test/resources/alert/detailedalert/no_active_alerts.json +++ b/src/test/resources/alert/detailedalert/no_active_alerts.json @@ -4,4 +4,4 @@ "ErrorCode": "25", "ErrorMessage": "There are no active alerts" } -} \ No newline at end of file +} diff --git a/src/test/resources/alert/detailedalert/no_active_alerts_for_filter.json b/src/test/resources/alert/detailedalert/no_active_alerts_for_filter.json index ae285751..efe328d6 100644 --- a/src/test/resources/alert/detailedalert/no_active_alerts_for_filter.json +++ b/src/test/resources/alert/detailedalert/no_active_alerts_for_filter.json @@ -4,4 +4,4 @@ "ErrorCode": "50", "ErrorMessage": "There are no active alerts based on your filter criteria" } -} \ No newline at end of file +} diff --git a/src/test/resources/alert/detailedalert/ok_no_alerts.json b/src/test/resources/alert/detailedalert/ok_no_alerts.json index 1b01c6fa..88f7d98c 100644 --- a/src/test/resources/alert/detailedalert/ok_no_alerts.json +++ b/src/test/resources/alert/detailedalert/ok_no_alerts.json @@ -3,4 +3,4 @@ "TimeStamp": "2026-07-28T12:00:00", "ErrorCode": "0" } -} \ No newline at end of file +} diff --git a/src/test/resources/alert/routestatus/bad_error_code.json b/src/test/resources/alert/routestatus/bad_error_code.json index 53b13a8b..88b52794 100644 --- a/src/test/resources/alert/routestatus/bad_error_code.json +++ b/src/test/resources/alert/routestatus/bad_error_code.json @@ -3,4 +3,4 @@ "TimeStamp": "2026-07-17T14:40:15", "ErrorCode": "notanumber" } -} \ No newline at end of file +} diff --git a/src/test/resources/alert/routestatus/blank_error_message.json b/src/test/resources/alert/routestatus/blank_error_message.json index 945234d9..490c9d11 100644 --- a/src/test/resources/alert/routestatus/blank_error_message.json +++ b/src/test/resources/alert/routestatus/blank_error_message.json @@ -4,4 +4,4 @@ "ErrorCode": "900", "ErrorMessage": "" } -} \ No newline at end of file +} diff --git a/src/test/resources/alert/routestatus/bus_success.json b/src/test/resources/alert/routestatus/bus_success.json index c012402e..8ccdd310 100644 --- a/src/test/resources/alert/routestatus/bus_success.json +++ b/src/test/resources/alert/routestatus/bus_success.json @@ -11,4 +11,4 @@ "RouteStatusColor": "000000" } } -} \ No newline at end of file +} diff --git a/src/test/resources/alert/routestatus/distinct_error_codes.json b/src/test/resources/alert/routestatus/distinct_error_codes.json index 73863f08..e5d589fe 100644 --- a/src/test/resources/alert/routestatus/distinct_error_codes.json +++ b/src/test/resources/alert/routestatus/distinct_error_codes.json @@ -3,4 +3,4 @@ "TimeStamp": "2026-07-17T14:40:15", "ErrorCode": ["0", "50"] } -} \ No newline at end of file +} diff --git a/src/test/resources/alert/routestatus/empty_route_info_array.json b/src/test/resources/alert/routestatus/empty_route_info_array.json index 8e210b04..41969af9 100644 --- a/src/test/resources/alert/routestatus/empty_route_info_array.json +++ b/src/test/resources/alert/routestatus/empty_route_info_array.json @@ -5,4 +5,4 @@ "ErrorMessage": null, "RouteInfo": [] } -} \ No newline at end of file +} diff --git a/src/test/resources/alert/routestatus/error_code_empty_array.json b/src/test/resources/alert/routestatus/error_code_empty_array.json index 1b81a401..46808a3e 100644 --- a/src/test/resources/alert/routestatus/error_code_empty_array.json +++ b/src/test/resources/alert/routestatus/error_code_empty_array.json @@ -3,4 +3,4 @@ "TimeStamp": "2026-07-17T14:40:15", "ErrorCode": [] } -} \ No newline at end of file +} diff --git a/src/test/resources/alert/routestatus/error_message_empty_array.json b/src/test/resources/alert/routestatus/error_message_empty_array.json index 0dab11b4..b04725c5 100644 --- a/src/test/resources/alert/routestatus/error_message_empty_array.json +++ b/src/test/resources/alert/routestatus/error_message_empty_array.json @@ -4,4 +4,4 @@ "ErrorCode": "101", "ErrorMessage": [] } -} \ No newline at end of file +} diff --git a/src/test/resources/alert/routestatus/error_no_message.json b/src/test/resources/alert/routestatus/error_no_message.json index 4ff40ff9..0a017d3e 100644 --- a/src/test/resources/alert/routestatus/error_no_message.json +++ b/src/test/resources/alert/routestatus/error_no_message.json @@ -3,4 +3,4 @@ "TimeStamp": "2026-07-17T14:40:15", "ErrorCode": "101" } -} \ No newline at end of file +} diff --git a/src/test/resources/alert/routestatus/error_null_message_element.json b/src/test/resources/alert/routestatus/error_null_message_element.json index 800a0c60..05c602b7 100644 --- a/src/test/resources/alert/routestatus/error_null_message_element.json +++ b/src/test/resources/alert/routestatus/error_null_message_element.json @@ -4,4 +4,4 @@ "ErrorCode": "101", "ErrorMessage": [null] } -} \ No newline at end of file +} diff --git a/src/test/resources/alert/routestatus/invalid_type_error.json b/src/test/resources/alert/routestatus/invalid_type_error.json index 0a9f1468..1dfb91af 100644 --- a/src/test/resources/alert/routestatus/invalid_type_error.json +++ b/src/test/resources/alert/routestatus/invalid_type_error.json @@ -4,4 +4,4 @@ "ErrorCode": "101", "ErrorMessage": "Invalid option for parameter 'type': Valid options are 'bus', 'rail', 'station' or 'systemwide'" } -} \ No newline at end of file +} diff --git a/src/test/resources/alert/routestatus/list_success.json b/src/test/resources/alert/routestatus/list_success.json index 44e48e53..9b47acf0 100644 --- a/src/test/resources/alert/routestatus/list_success.json +++ b/src/test/resources/alert/routestatus/list_success.json @@ -33,4 +33,4 @@ } ] } -} \ No newline at end of file +} diff --git a/src/test/resources/alert/routestatus/no_data_no_error.json b/src/test/resources/alert/routestatus/no_data_no_error.json index 9ca61e9e..4ee45370 100644 --- a/src/test/resources/alert/routestatus/no_data_no_error.json +++ b/src/test/resources/alert/routestatus/no_data_no_error.json @@ -2,4 +2,4 @@ "CTARoutes": { "TimeStamp": "2026-07-17T14:40:15" } -} \ No newline at end of file +} diff --git a/src/test/resources/alert/routestatus/no_results.json b/src/test/resources/alert/routestatus/no_results.json index 39c1d8c2..40dd851b 100644 --- a/src/test/resources/alert/routestatus/no_results.json +++ b/src/test/resources/alert/routestatus/no_results.json @@ -4,4 +4,4 @@ "ErrorCode": "50", "ErrorMessage": "There are no routes based on your filter criteria" } -} \ No newline at end of file +} diff --git a/src/test/resources/alert/routestatus/rail_success.json b/src/test/resources/alert/routestatus/rail_success.json index 84595a21..4aa296b7 100644 --- a/src/test/resources/alert/routestatus/rail_success.json +++ b/src/test/resources/alert/routestatus/rail_success.json @@ -24,4 +24,4 @@ } ] } -} \ No newline at end of file +} diff --git a/src/test/resources/bus/detour/empty_dtrs_array.json b/src/test/resources/bus/detour/empty_dtrs_array.json index cae8e0d9..37586a1a 100644 --- a/src/test/resources/bus/detour/empty_dtrs_array.json +++ b/src/test/resources/bus/detour/empty_dtrs_array.json @@ -2,4 +2,4 @@ "bustime-response": { "dtrs": [] } -} \ No newline at end of file +} diff --git a/src/test/resources/bus/direction/empty_directions_array.json b/src/test/resources/bus/direction/empty_directions_array.json index 6f88a064..afb106f7 100644 --- a/src/test/resources/bus/direction/empty_directions_array.json +++ b/src/test/resources/bus/direction/empty_directions_array.json @@ -2,4 +2,4 @@ "bustime-response": { "directions": [] } -} \ No newline at end of file +} diff --git a/src/test/resources/bus/locale/empty_locale_array.json b/src/test/resources/bus/locale/empty_locale_array.json index 5f97ace1..b94eccc0 100644 --- a/src/test/resources/bus/locale/empty_locale_array.json +++ b/src/test/resources/bus/locale/empty_locale_array.json @@ -2,4 +2,4 @@ "bustime-response": { "locale": [] } -} \ No newline at end of file +} diff --git a/src/test/resources/bus/pattern/empty_ptr_array.json b/src/test/resources/bus/pattern/empty_ptr_array.json index 9c76151c..a424747f 100644 --- a/src/test/resources/bus/pattern/empty_ptr_array.json +++ b/src/test/resources/bus/pattern/empty_ptr_array.json @@ -2,4 +2,4 @@ "bustime-response": { "ptr": [] } -} \ No newline at end of file +} diff --git a/src/test/resources/bus/prediction/empty_prd_array.json b/src/test/resources/bus/prediction/empty_prd_array.json index 5786cc98..f2922acc 100644 --- a/src/test/resources/bus/prediction/empty_prd_array.json +++ b/src/test/resources/bus/prediction/empty_prd_array.json @@ -2,4 +2,4 @@ "bustime-response": { "prd": [] } -} \ No newline at end of file +} diff --git a/src/test/resources/bus/route/empty_routes_array.json b/src/test/resources/bus/route/empty_routes_array.json index da893d85..adcbadc0 100644 --- a/src/test/resources/bus/route/empty_routes_array.json +++ b/src/test/resources/bus/route/empty_routes_array.json @@ -2,4 +2,4 @@ "bustime-response": { "routes": [] } -} \ No newline at end of file +} diff --git a/src/test/resources/bus/time/empty-error-array.json b/src/test/resources/bus/time/empty-error-array.json index 008e8f54..d69e6eed 100644 --- a/src/test/resources/bus/time/empty-error-array.json +++ b/src/test/resources/bus/time/empty-error-array.json @@ -2,4 +2,4 @@ "bustime-response": { "error": [] } -} \ No newline at end of file +} diff --git a/src/test/resources/bus/vehicle/empty_vehicle_array.json b/src/test/resources/bus/vehicle/empty_vehicle_array.json index 973dcc3d..d80b5740 100644 --- a/src/test/resources/bus/vehicle/empty_vehicle_array.json +++ b/src/test/resources/bus/vehicle/empty_vehicle_array.json @@ -2,4 +2,4 @@ "bustime-response": { "vehicle": [] } -} \ No newline at end of file +} From 3668a5662cc89022878d37522911d95680c746f6 Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Sat, 1 Aug 2026 16:08:27 -0500 Subject: [PATCH 57/60] README updates --- README.md | 46 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 40 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index fb147b4d..433319a3 100644 --- a/README.md +++ b/README.md @@ -6,30 +6,33 @@ ![Java Version](https://img.shields.io/badge/Java-21%2B-orange) [![License](https://img.shields.io/github/license/lbkulinski/cta4j-java-sdk)](LICENSE) -A lightweight Java SDK for interacting with the [Chicago Transit Authority (CTA)](https://www.transitchicago.com/) APIs — both Train Tracker and Bus Tracker. +A lightweight Java SDK for interacting with the [Chicago Transit Authority (CTA)](https://www.transitchicago.com/) APIs — Train Tracker, Bus Tracker, and Customer Alerts. Built for simplicity, reliability, and minimal external dependencies. --- -## 🚆 Overview +## 🏙️ Overview `cta4j-java-sdk` provides a clean, type-safe interface for accessing CTA's public transit data. -It wraps the official Train and Bus Tracker APIs with intuitive Java models and error handling. +It wraps the official Train Tracker, Bus Tracker, and Customer Alerts APIs with intuitive Java models and error +handling. **Features:** - Simple, dependency-light HTTP client (uses Apache HttpClient 5) - DTOs modeled as Java records -- Works with both **Train Tracker** and **Bus Tracker** APIs +- Works with the **Train Tracker**, **Bus Tracker**, and **Customer Alerts** APIs --- ## 🔑 Getting API Keys -You'll need a free API key from CTA to use the SDK. +You'll need a free API key from CTA to use the Train Tracker or Bus Tracker APIs. The Customer Alerts API is +unauthenticated and needs no key. - **Train Tracker API** → [Apply here](https://www.transitchicago.com/developers/traintrackerapply/) - **Bus Tracker API** → [Apply here](https://www.transitchicago.com/developers/bustracker/) +- **Customer Alerts API** → No API key is needed After applying, you'll receive an API key by email. Keep it safe — you'll use it when initializing the client. @@ -117,6 +120,38 @@ public final class Application { } ``` +### Fetch detailed alerts for a route ID + +> **Note:** `AlertApi` requires no API key — the CTA Customer Alerts API is unauthenticated. + +```java +import com.cta4j.alert.AlertApi; + +public final class Application { + public static void main(String[] args) { + AlertApi alertApi = AlertApi.builder() + .build(); + + alertApi.detailedAlerts() + .findByBusRouteId("70") + .forEach(alert -> System.out.printf( + "Alert ID: %s%nDescription: %s%nFrom: %s%nTo: %s%n%n", + alert.id(), + alert.shortDescription(), + alert.startTime(), + alert.endTime() + )); + + // Example output: + // Alert ID: 114946 + // Description: EB #70 buses will operate via Division, Wells, Oak, and Dearborn. WB buses will operate via Clark, Oak, Wells, and Division. + // From: 2026-08-01T12:30:00Z + // To: 2026-08-01T22:00:00Z + // ... + } +} +``` + --- ## 🧠 Design Goals @@ -129,7 +164,6 @@ public final class Application { ## 🛠️ Planned Improvements -- Add support for more API endpoints, like service alerts - Implement caching for frequently requested data - Add asynchronous request support From 7270e1eccfed6eaa0d608223a85172622f08f63f Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Sat, 1 Aug 2026 22:44:13 -0500 Subject: [PATCH 58/60] CHANGELOG.md updates --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb393f8c..0b66f261 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 to `vehicleId`, for consistency with `StopsApi`/`PatternsApi`'s equivalent methods. - Bumped `tools.jackson.core:jackson-databind` from **3.2.0** → **3.2.1** - Bumped `org.apache.httpcomponents.client5:httpclient5-fluent` from **5.6.1** → **5.6.2** +- Bumped `ch.qos.logback:logback-classic` from **1.5.38** → **1.6.0** ### Breaking Changes ⚠️ From b2bfcbd722ec917ffd158023fe5b2f79843cf55c Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Sat, 1 Aug 2026 22:50:40 -0500 Subject: [PATCH 59/60] Update Java version to 25 in build and release configurations --- .github/workflows/build.yaml | 2 +- .github/workflows/release.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 49b23e9b..64dd65b4 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -24,7 +24,7 @@ jobs: uses: actions/setup-java@v5.6.0 with: distribution: 'corretto' - java-version: '21' + java-version: '25' cache: 'maven' - name: Read project version from pom.xml diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index dd5f3f49..74cbfb7e 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -26,7 +26,7 @@ jobs: uses: actions/setup-java@v5.6.0 with: distribution: corretto - java-version: '21' + java-version: '25' server-id: central server-username: OSSRH_USERNAME server-password: OSSRH_PASSWORD From c3e9faaf6795b921da3ed82b55490c867a044f6f Mon Sep 17 00:00:00 2001 From: Logan Kulinski Date: Sat, 1 Aug 2026 23:06:08 -0500 Subject: [PATCH 60/60] Refactor LocationsApi methods --- .../java/com/cta4j/train/location/LocationsApi.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/cta4j/train/location/LocationsApi.java b/src/main/java/com/cta4j/train/location/LocationsApi.java index d5a28f20..d778b994 100644 --- a/src/main/java/com/cta4j/train/location/LocationsApi.java +++ b/src/main/java/com/cta4j/train/location/LocationsApi.java @@ -6,6 +6,7 @@ import org.jspecify.annotations.NullMarked; import java.util.List; +import java.util.Objects; /// Provides access to location-related endpoints of the CTA Train Tracker API. /// @@ -17,7 +18,9 @@ public interface LocationsApi { /// @return a [List] of [TrainLocations] for all lines, or an empty [List] if no train locations are found /// @throws Cta4jLocationsException if the API returns an error response or the response cannot be parsed default List list() { - return findByLines(List.of(TrainLine.values())); + List lines = List.of(TrainLine.values()); + + return this.findByLines(lines); } /// Retrieves train locations for the specified lines. @@ -37,6 +40,10 @@ default List list() { /// @throws NullPointerException if `line` is `null` /// @throws Cta4jLocationsException if the API returns an error response or the response cannot be parsed default List findByLine(TrainLine line) { - return findByLines(List.of(line)); + Objects.requireNonNull(line); + + List lines = List.of(line); + + return this.findByLines(lines); } }