MiniUri.java

  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}