1package de.gultsch.common;
2
3import com.google.common.base.MoreObjects;
4import com.google.common.base.Splitter;
5import com.google.common.base.Strings;
6import com.google.common.collect.ImmutableMap;
7import java.io.UnsupportedEncodingException;
8import java.net.URLDecoder;
9import java.util.Collections;
10import java.util.Locale;
11import java.util.Map;
12import org.checkerframework.checker.nullness.qual.NonNull;
13
14public class MiniUri {
15
16 private static final String EMPTY_STRING = "";
17
18 private final String raw;
19 private final String scheme;
20 private final String authority;
21 private final String path;
22 private final Map<String, String> parameter;
23
24 public MiniUri(final String uri) {
25 this.raw = uri;
26 final var schemeAndRest = Splitter.on(':').limit(2).splitToList(uri);
27 if (schemeAndRest.size() < 2) {
28 this.scheme = uri;
29 this.authority = null;
30 this.path = null;
31 this.parameter = Collections.emptyMap();
32 return;
33 }
34 this.scheme = schemeAndRest.get(0);
35 final var rest = schemeAndRest.get(1);
36 final var authorityPathAndQuery = Splitter.on('?').limit(2).splitToList(rest);
37 final var authorityPath = authorityPathAndQuery.get(0);
38 System.out.println("authorityPath " + authorityPath);
39 if (authorityPath.length() >= 2 && authorityPath.startsWith("//")) {
40 final var authorityPathParts =
41 Splitter.on('/').limit(2).splitToList(authorityPath.substring(2));
42 this.authority = authorityPathParts.get(0);
43 this.path = authorityPathParts.size() == 2 ? authorityPathParts.get(1) : null;
44 } else {
45 this.authority = null;
46 // TODO path ; style path components from something like geo uri
47 this.path = authorityPath;
48 }
49 if (authorityPathAndQuery.size() == 2) {
50 this.parameter = parseParameters(authorityPathAndQuery.get(1), getDelimiter(scheme));
51 } else {
52 this.parameter = Collections.emptyMap();
53 }
54 }
55
56 private static char getDelimiter(final String scheme) {
57 return switch (scheme) {
58 case "xmpp", "geo" -> ';';
59 default -> '&';
60 };
61 }
62
63 private static Map<String, String> parseParameters(final String query, final char separator) {
64 final ImmutableMap.Builder<String, String> builder = new ImmutableMap.Builder<>();
65 for (final String pair : Splitter.on(separator).split(query)) {
66 final String[] parts = pair.split("=", 2);
67 if (parts.length == 0) {
68 continue;
69 }
70 final String key = parts[0].toLowerCase(Locale.US);
71 if (parts.length == 2) {
72 try {
73 builder.put(key, URLDecoder.decode(parts[1], "UTF-8"));
74 } catch (final UnsupportedEncodingException e) {
75 builder.put(key, EMPTY_STRING);
76 }
77 } else {
78 builder.put(key, EMPTY_STRING);
79 }
80 }
81 return builder.build();
82 }
83
84 @NonNull
85 @Override
86 public String toString() {
87 return MoreObjects.toStringHelper(this)
88 .add("scheme", scheme)
89 .add("authority", authority)
90 .add("path", path)
91 .add("parameter", parameter)
92 .toString();
93 }
94
95 public String getScheme() {
96 return this.scheme;
97 }
98
99 public String getAuthority() {
100 return this.authority;
101 }
102
103 public String getPath() {
104 return Strings.isNullOrEmpty(this.path) || this.authority == null
105 ? this.path
106 : '/' + this.path;
107 }
108
109 public String getRaw() {
110 return this.raw;
111 }
112
113 public Map<String, String> getParameter() {
114 return this.parameter;
115 }
116}