1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 package org.apache.hc.core5.net;
28
29 import java.net.InetAddress;
30 import java.net.URI;
31 import java.net.URISyntaxException;
32 import java.net.UnknownHostException;
33 import java.nio.charset.Charset;
34 import java.nio.charset.StandardCharsets;
35 import java.util.ArrayList;
36 import java.util.Arrays;
37 import java.util.Collections;
38 import java.util.Iterator;
39 import java.util.List;
40
41 import org.apache.hc.core5.http.HttpHost;
42 import org.apache.hc.core5.http.NameValuePair;
43 import org.apache.hc.core5.http.message.BasicNameValuePair;
44 import org.apache.hc.core5.util.TextUtils;
45
46
47
48
49
50
51 public class URIBuilder {
52
53
54
55
56
57
58
59 public static URIBuilder localhost() throws UnknownHostException {
60 return new URIBuilder().setHost(InetAddress.getLocalHost());
61 }
62
63
64
65
66 public static URIBuilder loopbackAddress() {
67 return new URIBuilder().setHost(InetAddress.getLoopbackAddress());
68 }
69
70 private String scheme;
71 private String encodedSchemeSpecificPart;
72 private String encodedAuthority;
73 private String userInfo;
74 private String encodedUserInfo;
75 private String host;
76 private int port;
77 private String encodedPath;
78 private List<String> pathSegments;
79 private String encodedQuery;
80 private List<NameValuePair> queryParams;
81 private String query;
82 private Charset charset;
83 private String fragment;
84 private String encodedFragment;
85
86
87
88
89 public URIBuilder() {
90 super();
91 this.port = -1;
92 }
93
94
95
96
97
98
99
100 public URIBuilder(final String string) throws URISyntaxException {
101 this(new URI(string), null);
102 }
103
104
105
106
107
108 public URIBuilder(final URI uri) {
109 this(uri, null);
110 }
111
112
113
114
115
116
117
118 public URIBuilder(final String string, final Charset charset) throws URISyntaxException {
119 this(new URI(string), charset);
120 }
121
122
123
124
125
126 public URIBuilder(final URI uri, final Charset charset) {
127 super();
128 setCharset(charset);
129 digestURI(uri);
130 }
131
132 public URIBuilder setCharset(final Charset charset) {
133 this.charset = charset;
134 return this;
135 }
136
137 public Charset getCharset() {
138 return charset;
139 }
140
141 private List <NameValuePair> parseQuery(final String query, final Charset charset) {
142 if (query != null && !query.isEmpty()) {
143 return URLEncodedUtils.parse(query, charset);
144 }
145 return null;
146 }
147
148 private List <String> parsePath(final String path, final Charset charset) {
149 if (path != null && !path.isEmpty()) {
150 return URLEncodedUtils.parsePathSegments(path, charset);
151 }
152 return null;
153 }
154
155
156
157
158 public URI build() throws URISyntaxException {
159 return new URI(buildString());
160 }
161
162 private String buildString() {
163 final StringBuilder sb = new StringBuilder();
164 if (this.scheme != null) {
165 sb.append(this.scheme).append(':');
166 }
167 if (this.encodedSchemeSpecificPart != null) {
168 sb.append(this.encodedSchemeSpecificPart);
169 } else {
170 if (this.encodedAuthority != null) {
171 sb.append("//").append(this.encodedAuthority);
172 } else if (this.host != null) {
173 sb.append("//");
174 if (this.encodedUserInfo != null) {
175 sb.append(this.encodedUserInfo).append("@");
176 } else if (this.userInfo != null) {
177 encodeUserInfo(sb, this.userInfo);
178 sb.append("@");
179 }
180 if (InetAddressUtils.isIPv6Address(this.host)) {
181 sb.append("[").append(this.host).append("]");
182 } else {
183 sb.append(this.host);
184 }
185 if (this.port >= 0) {
186 sb.append(":").append(this.port);
187 }
188 }
189 if (this.encodedPath != null) {
190 sb.append(normalizePath(this.encodedPath, sb.length() == 0));
191 } else if (this.pathSegments != null) {
192 encodePath(sb, this.pathSegments);
193 }
194 if (this.encodedQuery != null) {
195 sb.append("?").append(this.encodedQuery);
196 } else if (this.queryParams != null && !this.queryParams.isEmpty()) {
197 sb.append("?");
198 encodeUrlForm(sb, this.queryParams);
199 } else if (this.query != null) {
200 sb.append("?");
201 encodeUric(sb, this.query);
202 }
203 }
204 if (this.encodedFragment != null) {
205 sb.append("#").append(this.encodedFragment);
206 } else if (this.fragment != null) {
207 sb.append("#");
208 encodeUric(sb, this.fragment);
209 }
210 return sb.toString();
211 }
212
213 private static String normalizePath(final String path, final boolean relative) {
214 String s = path;
215 if (TextUtils.isBlank(s)) {
216 return "";
217 }
218 if (!relative && !s.startsWith("/")) {
219 s = "/" + s;
220 }
221 return s;
222 }
223
224 private void digestURI(final URI uri) {
225 this.scheme = uri.getScheme();
226 this.encodedSchemeSpecificPart = uri.getRawSchemeSpecificPart();
227 this.encodedAuthority = uri.getRawAuthority();
228 this.host = uri.getHost();
229 this.port = uri.getPort();
230 this.encodedUserInfo = uri.getRawUserInfo();
231 this.userInfo = uri.getUserInfo();
232 this.encodedPath = uri.getRawPath();
233 this.pathSegments = parsePath(uri.getRawPath(), this.charset != null ? this.charset : StandardCharsets.UTF_8);
234 this.encodedQuery = uri.getRawQuery();
235 this.queryParams = parseQuery(uri.getRawQuery(), this.charset != null ? this.charset : StandardCharsets.UTF_8);
236 this.encodedFragment = uri.getRawFragment();
237 this.fragment = uri.getFragment();
238 }
239
240 private void encodeUserInfo(final StringBuilder buf, final String userInfo) {
241 URLEncodedUtils.encUserInfo(buf, userInfo, this.charset != null ? this.charset : StandardCharsets.UTF_8);
242 }
243
244 private void encodePath(final StringBuilder buf, final List<String> pathSegments) {
245 URLEncodedUtils.formatSegments(buf, pathSegments, this.charset != null ? this.charset : StandardCharsets.UTF_8);
246 }
247
248 private void encodeUrlForm(final StringBuilder buf, final List<NameValuePair> params) {
249 URLEncodedUtils.formatParameters(buf, params, this.charset != null ? this.charset : StandardCharsets.UTF_8);
250 }
251
252 private void encodeUric(final StringBuilder buf, final String fragment) {
253 URLEncodedUtils.encUric(buf, fragment, this.charset != null ? this.charset : StandardCharsets.UTF_8);
254 }
255
256
257
258
259
260
261 public URIBuilder setScheme(final String scheme) {
262 this.scheme = !TextUtils.isBlank(scheme) ? scheme : null;
263 return this;
264 }
265
266
267
268
269
270
271
272 public URIBuilder setUserInfo(final String userInfo) {
273 this.userInfo = !TextUtils.isBlank(userInfo) ? userInfo : null;
274 this.encodedSchemeSpecificPart = null;
275 this.encodedAuthority = null;
276 this.encodedUserInfo = null;
277 return this;
278 }
279
280
281
282
283
284
285
286 public URIBuilder setUserInfo(final String username, final String password) {
287 return setUserInfo(username + ':' + password);
288 }
289
290
291
292
293
294
295 public URIBuilder setHost(final InetAddress host) {
296 this.host = host != null ? host.getHostAddress() : null;
297 this.encodedSchemeSpecificPart = null;
298 this.encodedAuthority = null;
299 return this;
300 }
301
302
303
304
305
306
307 public URIBuilder setHost(final String host) {
308 this.host = !TextUtils.isBlank(host) ? host : null;
309 this.encodedSchemeSpecificPart = null;
310 this.encodedAuthority = null;
311 return this;
312 }
313
314
315
316
317
318
319
320 public URIBuilder setHttpHost(final HttpHost httpHost ) {
321 setScheme(httpHost.getSchemeName());
322 setHost(httpHost.getHostName());
323 setPort(httpHost.getPort());
324 return this;
325 }
326
327
328
329
330
331
332 public URIBuilder setPort(final int port) {
333 this.port = port < 0 ? -1 : port;
334 this.encodedSchemeSpecificPart = null;
335 this.encodedAuthority = null;
336 return this;
337 }
338
339
340
341
342
343
344 public URIBuilder setPath(final String path) {
345 return setPathSegments(path != null ? URLEncodedUtils.splitPathSegments(path) : null);
346 }
347
348
349
350
351
352
353 public URIBuilder setPathSegments(final String... pathSegments) {
354 this.pathSegments = pathSegments.length > 0 ? Arrays.asList(pathSegments) : null;
355 this.encodedSchemeSpecificPart = null;
356 this.encodedPath = null;
357 return this;
358 }
359
360
361
362
363
364
365 public URIBuilder setPathSegments(final List<String> pathSegments) {
366 this.pathSegments = pathSegments != null && pathSegments.size() > 0 ? new ArrayList<>(pathSegments) : null;
367 this.encodedSchemeSpecificPart = null;
368 this.encodedPath = null;
369 return this;
370 }
371
372
373
374
375
376
377 public URIBuilder removeQuery() {
378 this.queryParams = null;
379 this.query = null;
380 this.encodedQuery = null;
381 this.encodedSchemeSpecificPart = null;
382 return this;
383 }
384
385
386
387
388
389
390
391
392
393
394
395 public URIBuilder setParameters(final List <NameValuePair> nvps) {
396 if (this.queryParams == null) {
397 this.queryParams = new ArrayList<>();
398 } else {
399 this.queryParams.clear();
400 }
401 this.queryParams.addAll(nvps);
402 this.encodedQuery = null;
403 this.encodedSchemeSpecificPart = null;
404 this.query = null;
405 return this;
406 }
407
408
409
410
411
412
413
414
415
416
417
418 public URIBuilder addParameters(final List <NameValuePair> nvps) {
419 if (this.queryParams == null) {
420 this.queryParams = new ArrayList<>();
421 }
422 this.queryParams.addAll(nvps);
423 this.encodedQuery = null;
424 this.encodedSchemeSpecificPart = null;
425 this.query = null;
426 return this;
427 }
428
429
430
431
432
433
434
435
436
437
438
439 public URIBuilder setParameters(final NameValuePair... nvps) {
440 if (this.queryParams == null) {
441 this.queryParams = new ArrayList<>();
442 } else {
443 this.queryParams.clear();
444 }
445 Collections.addAll(this.queryParams, nvps);
446 this.encodedQuery = null;
447 this.encodedSchemeSpecificPart = null;
448 this.query = null;
449 return this;
450 }
451
452
453
454
455
456
457
458
459
460
461
462 public URIBuilder addParameter(final String param, final String value) {
463 if (this.queryParams == null) {
464 this.queryParams = new ArrayList<>();
465 }
466 this.queryParams.add(new BasicNameValuePair(param, value));
467 this.encodedQuery = null;
468 this.encodedSchemeSpecificPart = null;
469 this.query = null;
470 return this;
471 }
472
473
474
475
476
477
478
479
480
481
482
483 public URIBuilder setParameter(final String param, final String value) {
484 if (this.queryParams == null) {
485 this.queryParams = new ArrayList<>();
486 }
487 if (!this.queryParams.isEmpty()) {
488 for (final Iterator<NameValuePair> it = this.queryParams.iterator(); it.hasNext(); ) {
489 final NameValuePair nvp = it.next();
490 if (nvp.getName().equals(param)) {
491 it.remove();
492 }
493 }
494 }
495 this.queryParams.add(new BasicNameValuePair(param, value));
496 this.encodedQuery = null;
497 this.encodedSchemeSpecificPart = null;
498 this.query = null;
499 return this;
500 }
501
502
503
504
505
506
507 public URIBuilder clearParameters() {
508 this.queryParams = null;
509 this.encodedQuery = null;
510 this.encodedSchemeSpecificPart = null;
511 return this;
512 }
513
514
515
516
517
518
519
520
521
522
523
524 public URIBuilder setCustomQuery(final String query) {
525 this.query = !TextUtils.isBlank(query) ? query : null;
526 this.encodedQuery = null;
527 this.encodedSchemeSpecificPart = null;
528 this.queryParams = null;
529 return this;
530 }
531
532
533
534
535
536
537
538 public URIBuilder setFragment(final String fragment) {
539 this.fragment = !TextUtils.isBlank(fragment) ? fragment : null;
540 this.encodedFragment = null;
541 return this;
542 }
543
544 public boolean isAbsolute() {
545 return this.scheme != null;
546 }
547
548 public boolean isOpaque() {
549 return this.pathSegments == null && this.encodedPath == null;
550 }
551
552 public String getScheme() {
553 return this.scheme;
554 }
555
556 public String getUserInfo() {
557 return this.userInfo;
558 }
559
560 public String getHost() {
561 return this.host;
562 }
563
564 public int getPort() {
565 return this.port;
566 }
567
568 public boolean isPathEmpty() {
569 return (this.pathSegments == null || this.pathSegments.isEmpty()) &&
570 (this.encodedPath == null || this.encodedPath.isEmpty());
571 }
572
573 public List<String> getPathSegments() {
574 return this.pathSegments != null ? new ArrayList<>(this.pathSegments) : Collections.<String>emptyList();
575 }
576
577 public String getPath() {
578 if (this.pathSegments == null) {
579 return null;
580 }
581 final StringBuilder result = new StringBuilder();
582 for (final String segment : this.pathSegments) {
583 result.append('/').append(segment);
584 }
585 return result.toString();
586 }
587
588 public boolean isQueryEmpty() {
589 return (this.queryParams == null || this.queryParams.isEmpty()) && this.encodedQuery == null;
590 }
591
592 public List<NameValuePair> getQueryParams() {
593 return this.queryParams != null ? new ArrayList<>(this.queryParams) : Collections.<NameValuePair>emptyList();
594 }
595
596 public String getFragment() {
597 return this.fragment;
598 }
599
600 @Override
601 public String toString() {
602 return buildString();
603 }
604
605 }