Added methods for managing uri query parameters
Change-Id: Ic98c1bd159740dd4d895889079f9f2abae4fc2b9
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index 6f4144b..3b21590 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -28,8 +28,10 @@
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.RandomAccess;
+import java.util.Set;
/**
* Immutable URI reference. A URI reference includes a URI and a fragment, the
@@ -47,7 +49,7 @@
/*
This class aims to do as little up front work as possible. To accomplish
- that, we vary the implementation dependending on what the user passes in.
+ that, we vary the implementation depending on what the user passes in.
For example, we have one implementation if the user passes in a
URI string (StringUri) and another if the user passes in the
individual components (OpaqueUri).
@@ -1261,6 +1263,8 @@
*
* <p>An opaque URI follows this pattern:
* {@code <scheme>:<opaque part>#<fragment>}
+ *
+ * <p>Use {@link Uri#buildUpon()} to obtain a builder representing an existing URI.
*/
public static final class Builder {
@@ -1447,6 +1451,13 @@
}
/**
+ * Clears the the previously set query.
+ */
+ public Builder clearQuery() {
+ return query((Part) null);
+ }
+
+ /**
* Constructs a Uri with the current attributes.
*
* @throws UnsupportedOperationException if the URI is opaque and the
@@ -1491,19 +1502,60 @@
}
/**
+ * Returns a set of the unique names of all query parameters. Iterating
+ * over the set will return the names in order of their first occurrence.
+ *
+ * @throws UnsupportedOperationException if this isn't a hierarchical URI
+ *
+ * @return a set of decoded names
+ */
+ public Set<String> getQueryParameterNames() {
+ if (isOpaque()) {
+ throw new UnsupportedOperationException(NOT_HIERARCHICAL);
+ }
+
+ String query = getEncodedQuery();
+ if (query == null) {
+ return Collections.emptySet();
+ }
+
+ Set<String> names = new LinkedHashSet<String>();
+ int start = 0;
+ do {
+ int next = query.indexOf('&', start);
+ int end = (next == -1) ? query.length() : next;
+
+ int separator = query.indexOf('=', start);
+ if (separator > end || separator == -1) {
+ separator = end;
+ }
+
+ String name = query.substring(start, separator);
+ names.add(decode(name));
+
+ // Move start to end of name.
+ start = end + 1;
+ } while (start < query.length());
+
+ return Collections.unmodifiableSet(names);
+ }
+
+ /**
* Searches the query string for parameter values with the given key.
*
* @param key which will be encoded
*
* @throws UnsupportedOperationException if this isn't a hierarchical URI
* @throws NullPointerException if key is null
- *
* @return a list of decoded values
*/
public List<String> getQueryParameters(String key) {
if (isOpaque()) {
throw new UnsupportedOperationException(NOT_HIERARCHICAL);
}
+ if (key == null) {
+ throw new NullPointerException("key");
+ }
String query = getEncodedQuery();
if (query == null) {
@@ -1517,39 +1569,34 @@
throw new AssertionError(e);
}
- // Prepend query with "&" making the first parameter the same as the
- // rest.
- query = "&" + query;
-
- // Parameter prefix.
- String prefix = "&" + encodedKey + "=";
-
ArrayList<String> values = new ArrayList<String>();
int start = 0;
- int length = query.length();
- while (start < length) {
- start = query.indexOf(prefix, start);
+ do {
+ int nextAmpersand = query.indexOf('&', start);
+ int end = nextAmpersand != -1 ? nextAmpersand : query.length();
- if (start == -1) {
- // No more values.
+ int separator = query.indexOf('=', start);
+ if (separator > end || separator == -1) {
+ separator = end;
+ }
+
+ if (separator - start == encodedKey.length()
+ && query.regionMatches(start, encodedKey, 0, encodedKey.length())) {
+ if (separator == end) {
+ values.add("");
+ } else {
+ values.add(decode(query.substring(separator + 1, end)));
+ }
+ }
+
+ // Move start to end of name.
+ if (nextAmpersand != -1) {
+ start = nextAmpersand + 1;
+ } else {
break;
}
-
- // Move start to start of value.
- start += prefix.length();
-
- // Find end of value.
- int end = query.indexOf('&', start);
- if (end == -1) {
- end = query.length();
- }
-
- String value = query.substring(start, end);
- values.add(decode(value));
-
- start = end;
- }
+ } while (true);
return Collections.unmodifiableList(values);
}
@@ -1560,7 +1607,6 @@
* @param key which will be encoded
* @throws UnsupportedOperationException if this isn't a hierarchical URI
* @throws NullPointerException if key is null
- *
* @return the decoded value or null if no parameter is found
*/
public String getQueryParameter(String key) {
@@ -1577,34 +1623,33 @@
}
final String encodedKey = encode(key, null);
- final int encodedKeyLength = encodedKey.length();
+ final int length = query.length();
+ int start = 0;
+ do {
+ int nextAmpersand = query.indexOf('&', start);
+ int end = nextAmpersand != -1 ? nextAmpersand : length;
- int encodedKeySearchIndex = 0;
- final int encodedKeySearchEnd = query.length() - (encodedKeyLength + 1);
+ int separator = query.indexOf('=', start);
+ if (separator > end || separator == -1) {
+ separator = end;
+ }
- while (encodedKeySearchIndex <= encodedKeySearchEnd) {
- int keyIndex = query.indexOf(encodedKey, encodedKeySearchIndex);
- if (keyIndex == -1) {
- break;
- }
- final int equalsIndex = keyIndex + encodedKeyLength;
- if (equalsIndex >= query.length()) {
- break;
- }
- if (query.charAt(equalsIndex) != '=') {
- encodedKeySearchIndex = equalsIndex + 1;
- continue;
- }
- if (keyIndex == 0 || query.charAt(keyIndex - 1) == '&') {
- int end = query.indexOf('&', equalsIndex);
- if (end == -1) {
- end = query.length();
+ if (separator - start == encodedKey.length()
+ && query.regionMatches(start, encodedKey, 0, encodedKey.length())) {
+ if (separator == end) {
+ return "";
+ } else {
+ return decode(query.substring(separator + 1, end));
}
- return decode(query.substring(equalsIndex + 1, end));
- } else {
- encodedKeySearchIndex = equalsIndex + 1;
}
- }
+
+ // Move start to end of name.
+ if (nextAmpersand != -1) {
+ start = nextAmpersand + 1;
+ } else {
+ break;
+ }
+ } while (true);
return null;
}