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