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