MiniUri.java

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