11package io .ipgeolocation .sdk ;
22
3+ import io .ipgeolocation .sdk .internal .Compat ;
4+ import java .util .ArrayList ;
35import java .util .Collections ;
46import java .util .LinkedHashMap ;
57import java .util .List ;
68import java .util .Map ;
9+ import java .util .Objects ;
710
811/**
9- * Metadata extracted from IP Geolocation API response headers and SDK execution.
10- *
11- * @param creditsCharged parsed value of {@code X-Credits-Charged}, or {@code null} when missing
12- * or non-numeric
13- * @param successfulRecords parsed value of {@code X-Successful-Record}, or {@code null} when
14- * missing or non-numeric
15- * @param statusCode HTTP status code of the final response
16- * @param durationMs total request duration in milliseconds
17- * @param rawHeaders immutable response header map with original header names
12+ * Metadata extracted from IPGeolocation API response headers and SDK execution.
1813 */
19- public record ApiResponseMetadata (
20- Integer creditsCharged ,
21- Integer successfulRecords ,
22- int statusCode ,
23- long durationMs ,
24- Map <String , List <String >> rawHeaders ) {
14+ public final class ApiResponseMetadata {
15+ private final Integer creditsCharged ;
16+ private final Integer successfulRecords ;
17+ private final int statusCode ;
18+ private final long durationMs ;
19+ private final Map <String , List <String >> rawHeaders ;
2520
26- public ApiResponseMetadata {
21+ public ApiResponseMetadata (
22+ Integer creditsCharged ,
23+ Integer successfulRecords ,
24+ int statusCode ,
25+ long durationMs ,
26+ Map <String , List <String >> rawHeaders ) {
2727 if (statusCode < 100 || statusCode > 599 ) {
2828 throw new IllegalArgumentException ("statusCode must be between 100 and 599" );
2929 }
3030 if (durationMs < 0 ) {
3131 throw new IllegalArgumentException ("durationMs must be >= 0" );
3232 }
33- rawHeaders = normalizeHeaders (rawHeaders );
33+ this .creditsCharged = creditsCharged ;
34+ this .successfulRecords = successfulRecords ;
35+ this .statusCode = statusCode ;
36+ this .durationMs = durationMs ;
37+ this .rawHeaders = normalizeHeaders (rawHeaders );
38+ }
39+
40+ public Integer creditsCharged () {
41+ return creditsCharged ;
42+ }
43+
44+ public Integer successfulRecords () {
45+ return successfulRecords ;
46+ }
47+
48+ public int statusCode () {
49+ return statusCode ;
50+ }
51+
52+ public long durationMs () {
53+ return durationMs ;
54+ }
55+
56+ public Map <String , List <String >> rawHeaders () {
57+ return rawHeaders ;
3458 }
3559
3660 /**
@@ -41,7 +65,7 @@ public record ApiResponseMetadata(
4165 * @throws IllegalArgumentException when name is null or blank
4266 */
4367 public List <String > headerValues (String name ) {
44- if (name == null || name .isBlank ()) {
68+ if (Compat .isBlank (name )) {
4569 throw new IllegalArgumentException ("header name must not be blank" );
4670 }
4771 for (Map .Entry <String , List <String >> entry : rawHeaders .entrySet ()) {
@@ -50,7 +74,7 @@ public List<String> headerValues(String name) {
5074 return entry .getValue ();
5175 }
5276 }
53- return List . of ();
77+ return Collections . emptyList ();
5478 }
5579
5680 /**
@@ -67,19 +91,60 @@ public String firstHeaderValue(String name) {
6791
6892 private static Map <String , List <String >> normalizeHeaders (Map <String , List <String >> headers ) {
6993 if (headers == null || headers .isEmpty ()) {
70- return Map . of ();
94+ return Collections . emptyMap ();
7195 }
7296 Map <String , List <String >> normalized = new LinkedHashMap <>(headers .size ());
73- headers .forEach (
74- (name , values ) -> {
75- if (name == null ) {
76- return ;
77- }
78- normalized .put (name , values == null || values .isEmpty () ? List .of () : List .copyOf (values ));
79- });
97+ for (Map .Entry <String , List <String >> entry : headers .entrySet ()) {
98+ String name = entry .getKey ();
99+ List <String > values = entry .getValue ();
100+ if (name == null ) {
101+ continue ;
102+ }
103+ normalized .put (
104+ name ,
105+ values == null || values .isEmpty ()
106+ ? Collections .<String >emptyList ()
107+ : Collections .unmodifiableList (new ArrayList <String >(values )));
108+ }
80109 if (normalized .isEmpty ()) {
81- return Map . of ();
110+ return Collections . emptyMap ();
82111 }
83112 return Collections .unmodifiableMap (normalized );
84113 }
114+
115+ @ Override
116+ public boolean equals (Object other ) {
117+ if (this == other ) {
118+ return true ;
119+ }
120+ if (!(other instanceof ApiResponseMetadata )) {
121+ return false ;
122+ }
123+ ApiResponseMetadata that = (ApiResponseMetadata ) other ;
124+ return statusCode == that .statusCode
125+ && durationMs == that .durationMs
126+ && Objects .equals (creditsCharged , that .creditsCharged )
127+ && Objects .equals (successfulRecords , that .successfulRecords )
128+ && Objects .equals (rawHeaders , that .rawHeaders );
129+ }
130+
131+ @ Override
132+ public int hashCode () {
133+ return Objects .hash (creditsCharged , successfulRecords , statusCode , durationMs , rawHeaders );
134+ }
135+
136+ @ Override
137+ public String toString () {
138+ return "ApiResponseMetadata{creditsCharged="
139+ + creditsCharged
140+ + ", successfulRecords="
141+ + successfulRecords
142+ + ", statusCode="
143+ + statusCode
144+ + ", durationMs="
145+ + durationMs
146+ + ", rawHeaders="
147+ + rawHeaders
148+ + '}' ;
149+ }
85150}
0 commit comments