• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

coditory / quark-uri / #3

pending completion
#3

push

github-actions

ogesaku
init

1212 of 1212 new or added lines in 21 files covered. (100.0%)

926 of 1212 relevant lines covered (76.4%)

0.76 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

67.82
/src/main/java/com/coditory/quark/uri/UriComponents.java
1
package com.coditory.quark.uri;
2

3
import org.jetbrains.annotations.NotNull;
4
import org.jetbrains.annotations.Nullable;
5

6
import java.net.URI;
7
import java.net.URL;
8
import java.util.LinkedHashMap;
9
import java.util.List;
10
import java.util.Map;
11
import java.util.Objects;
12
import java.util.stream.Stream;
13

14
import static com.coditory.quark.uri.Nullable.mapNotNull;
15
import static com.coditory.quark.uri.Nullable.onNotNull;
16
import static com.coditory.quark.uri.Preconditions.expectNonBlank;
17
import static com.coditory.quark.uri.Preconditions.expectNonNull;
18
import static com.coditory.quark.uri.Strings.isNotNullOrEmpty;
19
import static com.coditory.quark.uri.Strings.isNullOrEmpty;
20
import static com.coditory.quark.uri.UriPartValidator.checkPort;
21
import static com.coditory.quark.uri.UriRfc.FRAGMENT;
22
import static com.coditory.quark.uri.UriRfc.PATH_SEGMENT;
23
import static com.coditory.quark.uri.UriRfc.QUERY_PARAM;
24
import static com.coditory.quark.uri.UriRfc.SCHEME;
25
import static com.coditory.quark.uri.UriRfc.SCHEME_SPECIFIC_PART;
26
import static com.coditory.quark.uri.UriRfc.USER_INFO;
27
import static java.util.Collections.unmodifiableMap;
28
import static java.util.stream.Collectors.joining;
29
import static java.util.stream.Collectors.toMap;
30

31
public final class UriComponents {
32
    @NotNull
33
    public static UriComponents fromUri(@NotNull String uri) {
34
        expectNonNull(uri, "uri");
1✔
35
        return builderFromUri(uri).buildUriComponents();
1✔
36
    }
37

38
    @Nullable
39
    public static UriComponents fromUriOrNull(@Nullable String uri) {
40
        if (uri == null) {
×
41
            return null;
×
42
        }
43
        try {
44
            return fromUri(uri);
×
45
        } catch (RuntimeException e) {
×
46
            return null;
×
47
        }
48
    }
49

50
    @NotNull
51
    public static UriComponents fromHttpUrl(@NotNull String url) {
52
        expectNonNull(url, "url");
1✔
53
        return builderFromHttpUrl(url).buildUriComponents();
1✔
54
    }
55

56
    @Nullable
57
    static UriComponents fromHttpUrlOrNull(@Nullable String url) {
58
        if (url == null) {
×
59
            return null;
×
60
        }
61
        try {
62
            return fromHttpUrl(url);
×
63
        } catch (RuntimeException e) {
×
64
            return null;
×
65
        }
66
    }
67

68
    @NotNull
69
    public static UriComponents from(@NotNull URI uri) {
70
        expectNonNull(uri, "uri");
×
71
        return builderFrom(uri).buildUriComponents();
×
72
    }
73

74
    @NotNull
75
    public static UriComponents from(@NotNull URL url) {
76
        expectNonNull(url, "url");
×
77
        return builderFrom(url).buildUriComponents();
×
78
    }
79

80
    @NotNull
81
    public static UriBuilder builder() {
82
        return new UriBuilder();
×
83
    }
84

85
    @NotNull
86
    public static UriBuilder builderFrom(@NotNull UriComponents uriComponents) {
87
        expectNonNull(uriComponents, "uriComponents");
×
88
        return UriBuilder.from(uriComponents);
×
89
    }
90

91
    @NotNull
92
    public static UriBuilder builderFrom(@NotNull URL url) {
93
        expectNonNull(url, "url");
×
94
        return UriBuilder.from(url);
×
95
    }
96

97
    @NotNull
98
    public static UriBuilder builderFrom(@NotNull URI uri) {
99
        expectNonNull(uri, "uri");
×
100
        return UriBuilder.from(uri);
×
101
    }
102

103
    @NotNull
104
    public static UriBuilder builderFromUri(@NotNull String uri) {
105
        expectNonNull(uri, "uri");
1✔
106
        return UriBuilder.fromUri(uri);
1✔
107
    }
108

109
    @NotNull
110
    public static UriBuilder builderFromHttpUrl(@NotNull String url) {
111
        expectNonNull(url, "url");
1✔
112
        return UriBuilder.fromHttpUrl(url);
1✔
113
    }
114

115
    static UriComponents buildOpaque(
116
            String scheme,
117
            String ssp,
118
            @Nullable String fragment
119
    ) {
120
        expectNonBlank(scheme, "scheme");
1✔
121
        expectNonBlank(ssp, "ssp");
1✔
122
        return new UriComponents(scheme, ssp, null, null, -1, false, false, null, null, fragment);
1✔
123
    }
124

125
    static UriComponents buildHierarchical(
126
            @Nullable String scheme,
127
            @Nullable String userInfo,
128
            @Nullable String host,
129
            int port,
130
            boolean protocolRelative,
131
            boolean rootRelative,
132
            @Nullable List<String> pathSegments,
133
            @Nullable Map<String, List<String>> queryParams,
134
            @Nullable String fragment
135
    ) {
136
        if (isNullOrEmpty(host)) {
1✔
137
            if (isNotNullOrEmpty(userInfo)) {
1✔
138
                throw new InvalidUriException("URI with user info must include host");
1✔
139
            }
140
            if (port >= 0) {
1✔
141
                throw new InvalidUriException("URI with port must include host");
1✔
142
            }
143
        }
144
        if (scheme != null && protocolRelative) {
1✔
145
            throw new InvalidUriException("URI cannot be protocol relative and have a scheme");
×
146
        }
147
        onNotNull(scheme, UriPartValidator::checkScheme);
1✔
148
        onNotNull(host, UriPartValidator::checkHost);
1✔
149
        if (port >= 0) {
1✔
150
            checkPort(port);
1✔
151
        }
152
        return new UriComponents(scheme, null, userInfo, host, port, protocolRelative, rootRelative, pathSegments, queryParams, fragment);
1✔
153
    }
154

155
    private final String ssp;
156
    private final String scheme;
157
    private final String userInfo;
158
    private final String host;
159
    private final int port;
160
    private final boolean protocolRelative;
161
    private final boolean rootPath;
162
    private final List<String> pathSegments;
163
    private final Map<String, List<String>> queryParams;
164
    private final String fragment;
165

166
    private UriComponents(
167
            String scheme,
168
            String ssp,
169
            String userInfo,
170
            String host,
171
            int port,
172
            boolean protocolRelative,
173
            boolean rootPath, List<String> pathSegments,
174
            Map<String, List<String>> queryParams,
175
            String fragment
176
    ) {
1✔
177
        this.scheme = scheme;
1✔
178
        this.ssp = ssp;
1✔
179
        this.userInfo = userInfo;
1✔
180
        this.host = host;
1✔
181
        this.port = port;
1✔
182
        this.protocolRelative = protocolRelative;
1✔
183
        this.rootPath = rootPath;
1✔
184
        this.pathSegments = pathSegments == null ? List.of() : List.copyOf(pathSegments);
1✔
185
        this.queryParams = queryParams == null ? Map.of() : unmodifiableMap(new LinkedHashMap<>(queryParams));
1✔
186
        this.fragment = fragment;
1✔
187
    }
1✔
188

189
    public boolean isOpaque() {
190
        return ssp != null;
1✔
191
    }
192

193
    @Nullable
194
    public String getSchemeSpecificPart() {
195
        return ssp;
1✔
196
    }
197

198
    @Nullable
199
    public String getScheme() {
200
        return scheme;
1✔
201
    }
202

203
    @Nullable
204
    public String getUserInfo() {
205
        return userInfo;
1✔
206
    }
207

208
    @Nullable
209
    public String getHost() {
210
        return host;
1✔
211
    }
212

213
    public int getPort() {
214
        return port;
1✔
215
    }
216

217
    public boolean isRootPath() {
218
        return rootPath;
1✔
219
    }
220

221
    public boolean isProtocolRelative() {
222
        return protocolRelative;
1✔
223
    }
224

225
    @NotNull
226
    public List<String> getPathSegments() {
227
        return pathSegments;
1✔
228
    }
229

230
    @NotNull
231
    public Map<String, List<String>> getQueryParams() {
232
        return queryParams;
1✔
233
    }
234

235
    @NotNull
236
    public Map<String, String> getSingleValueQueryParams() {
237
        return queryParams.entrySet()
1✔
238
                .stream()
1✔
239
                .collect(toMap(Map.Entry::getKey, e -> e.getValue().get(0)));
1✔
240
    }
241

242
    @Nullable
243
    public UriAuthority getUriAuthority() {
244
        if (isOpaque()) {
1✔
245
            return null;
1✔
246
        }
247
        UriAuthority authority = UriAuthority.of(userInfo, host, port);
1✔
248
        return authority.isEmpty() ? null : authority;
1✔
249
    }
250

251
    @Nullable
252
    public String getQueryString() {
253
        if (this.queryParams.isEmpty()) {
1✔
254
            return null;
1✔
255
        }
256
        StringBuilder queryBuilder = new StringBuilder();
1✔
257
        this.queryParams.forEach((name, values) -> {
1✔
258
            if (values == null || values.isEmpty()) {
1✔
259
                if (queryBuilder.length() != 0) {
×
260
                    queryBuilder.append('&');
×
261
                }
262
                queryBuilder.append(QUERY_PARAM.encode(name));
×
263
            } else {
264
                for (Object value : values) {
1✔
265
                    if (queryBuilder.length() != 0) {
1✔
266
                        queryBuilder.append('&');
1✔
267
                    }
268
                    queryBuilder.append(QUERY_PARAM.encode(name))
1✔
269
                            .append('=')
1✔
270
                            .append(QUERY_PARAM.encode(value.toString()));
1✔
271
                }
1✔
272
            }
273
        });
1✔
274
        return queryBuilder.toString();
1✔
275
    }
276

277
    @Nullable
278
    public String getPath() {
279
        if (pathSegments.isEmpty()) {
1✔
280
            return null;
1✔
281
        }
282
        String path = pathSegments.stream()
1✔
283
                .map(PATH_SEGMENT::encode)
1✔
284
                .collect(joining("/"));
1✔
285
        String prefix = rootPath ? "/" : "";
1✔
286
        return prefix + path;
1✔
287
    }
288

289
    @Nullable
290
    public String getFragment() {
291
        return fragment;
1✔
292
    }
293

294
    public boolean isHttpUrl() {
295
        return !isOpaque() && ("http".equals(scheme) || "https".equals(scheme));
1✔
296
    }
297

298
    public boolean isValidHttpUrl() {
299
        return UrlValidator.isValidUrl(this);
1✔
300
    }
301

302
    @NotNull
303
    public URL toUrl() {
304
        try {
305
            return new URL(toUriString());
1✔
306
        } catch (Exception e) {
×
307
            throw new IllegalStateException("Could not build URI", e);
×
308
        }
309
    }
310

311
    @NotNull
312
    public URI toUri() {
313
        try {
314
            return new URI(toUriString());
1✔
315
        } catch (Exception e) {
×
316
            throw new IllegalStateException("Could not build URI", e);
×
317
        }
318
    }
319

320
    @NotNull
321
    public String toUriString() {
322
        return ssp != null
1✔
323
                ? toOpaqueUriString()
1✔
324
                : toHierarchicalUriString();
1✔
325
    }
326

327
    private String toOpaqueUriString() {
328
        StringBuilder uriBuilder = new StringBuilder();
1✔
329
        if (scheme != null) {
1✔
330
            uriBuilder.append(SCHEME.encode(scheme))
1✔
331
                    .append(':');
1✔
332
        }
333
        uriBuilder.append(SCHEME_SPECIFIC_PART.encode(ssp));
1✔
334
        if (fragment != null) {
1✔
335
            uriBuilder.append('#')
1✔
336
                    .append(FRAGMENT.encode(fragment));
1✔
337
        }
338
        return uriBuilder.toString();
1✔
339
    }
340

341
    private String toHierarchicalUriString() {
342
        StringBuilder uriBuilder = new StringBuilder();
1✔
343
        if (scheme != null) {
1✔
344
            uriBuilder.append(SCHEME.encode(scheme))
1✔
345
                    .append("://");
1✔
346
        } else if (protocolRelative) {
1✔
347
            uriBuilder.append("//");
1✔
348
        }
349
        if (userInfo != null || host != null) {
1✔
350
            if (userInfo != null) {
1✔
351
                uriBuilder.append(USER_INFO.encode(this.userInfo))
1✔
352
                        .append('@');
1✔
353
            }
354
            if (host != null) {
1✔
355
                uriBuilder.append(this.host);
1✔
356
            }
357
            if (port != -1) {
1✔
358
                uriBuilder.append(':')
1✔
359
                        .append(this.port);
1✔
360
            }
361
        }
362
        String path = getPath();
1✔
363
        String query = getQueryString();
1✔
364
        if (path != null) {
1✔
365
            if (!path.equals("/") || query != null || fragment != null || host == null) {
1✔
366
                uriBuilder.append(path);
1✔
367
            }
368
        } else if (rootPath && host == null) {
1✔
369
            uriBuilder.append("/");
1✔
370
        }
371
        if (query != null) {
1✔
372
            uriBuilder.append('?')
1✔
373
                    .append(query);
1✔
374
        }
375
        if (fragment != null) {
1✔
376
            uriBuilder.append('#')
1✔
377
                    .append(FRAGMENT.encode(fragment));
1✔
378
        }
379
        return uriBuilder.toString();
1✔
380
    }
381

382
    @Override
383
    public boolean equals(Object o) {
384
        if (this == o) return true;
×
385
        if (o == null || getClass() != o.getClass()) return false;
×
386
        UriComponents that = (UriComponents) o;
×
387
        return port == that.port
×
388
                && protocolRelative == that.protocolRelative
389
                && rootPath == that.rootPath
390
                && Objects.equals(ssp, that.ssp)
×
391
                && Objects.equals(scheme, that.scheme)
×
392
                && Objects.equals(userInfo, that.userInfo)
×
393
                && Objects.equals(host, that.host)
×
394
                && Objects.equals(pathSegments, that.pathSegments)
×
395
                && Objects.equals(queryParams, that.queryParams)
×
396
                && Objects.equals(fragment, that.fragment);
×
397
    }
398

399
    @Override
400
    public int hashCode() {
401
        return Objects.hash(
×
402
                ssp, scheme, userInfo, host, port, protocolRelative, rootPath, pathSegments, queryParams, fragment
×
403
        );
404
    }
405

406
    @Override
407
    public String toString() {
408
        String content = Stream.of(
×
409
                        mapNotNull(ssp, it -> "ssp=\"" + it + '"', ""),
×
410
                        mapNotNull(scheme, it -> "scheme=\"" + it + '"', ""),
×
411
                        mapNotNull(userInfo, it -> "userInfo=\"" + it + '"', ""),
×
412
                        mapNotNull(host, it -> "host=\"" + it + '"', ""),
×
413
                        port != Ports.SCHEME_DEFAULT_PORT_NUMBER ? ("port=" + port) : "",
×
414
                        protocolRelative ? "protocolRelative=true" : "",
×
415
                        host == null && rootPath ? "rootPath=true" : "",
×
416
                        !pathSegments.isEmpty() ? "pathSegments=" + pathSegments : "",
×
417
                        !queryParams.isEmpty() ? "queryParams=" + queryParams : "",
×
418
                        mapNotNull(fragment, it -> "fragment=\"" + it + '"', "")
×
419
                )
420
                .filter(chunk -> !chunk.isEmpty())
×
421
                .collect(joining(", "));
×
422
        return "UriComponents{" + content + '}';
×
423
    }
424
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc