55
66package software .amazon .smithy .java .io .uri ;
77
8- import java .util .ArrayList ;
98import java .util .HashSet ;
109import java .util .List ;
1110import java .util .Map ;
12- import java .util .Set ;
1311
1412/**
1513 * Used to build a query string from key value pair parameters.
1614 */
1715public final class QueryStringBuilder {
1816
19- private final List <String > values = new ArrayList <>();
20- private final Set <String > keysFromHttpQuery = new HashSet <>();
17+ private final StringBuilder builder = new StringBuilder ();
18+ private final HashSet <String > httpQueryKeys = new HashSet <>();
19+ private boolean empty = true ;
2120
2221 /**
23- * Clears the contents of the query string builder.
22+ * Clears the contents of the query string builder so it can be reused .
2423 */
2524 public void clear () {
26- values .clear ();
25+ builder .setLength (0 );
26+ empty = true ;
27+ httpQueryKeys .clear ();
2728 }
2829
2930 /**
3031 * Add a query string parameter and value to the query string.
31- * <p>
32- * The given key and value will be percent-encoded. If the value is already percent-encoded, it will be
33- * double percent-encoded.
32+ *
33+ * <p> The given key and value are percent-encoded. If the value is already
34+ * percent-encoded, it will be double percent-encoded.
3435 *
3536 * @param key Key of the parameter.
36- * @param value Value of the parameter (or null).
37+ * @param value Value of the parameter (or null, which appends {@code key=} ).
3738 */
3839 public void add (String key , String value ) {
39- values .add (key );
40- keysFromHttpQuery .add (key );
41- values .add (value );
40+ append (key , value );
41+ httpQueryKeys .add (key );
4242 }
4343
4444 /**
45- * Add a query string parameter and value to the query string comes from httpQueryParams trait.
46- * <p>
47- * The given key and value will be percent-encoded. If the value is already percent-encoded, it will be
48- * double percent-encoded. Query string parameters from httpQuery should take precedence if there are
49- * duplicate keys from @httpQuery.
45+ * Add multiple values for the same key.
5046 *
51- * @param key Key of the parameter.
52- * @param value Value of the parameter (or null ).
47+ * @param key Key of the parameter.
48+ * @param values Values to add (each is added as a separate {@code key=value} pair ).
5349 */
54- public void addForQueryParams (String key , String value ) {
55- if (!keysFromHttpQuery .contains (key )) {
56- values .add (key );
57- values .add (value );
50+ public void add (String key , List <String > values ) {
51+ for (String v : values ) {
52+ add (key , v );
5853 }
5954 }
6055
6156 /**
62- * Add a query string parameter and values to the query string.
63- * <p>
64- * The given key and values will be percent-encoded. If the values are already percent-encoded, it will be
65- * double percent-encoded.
57+ * Add all entries from a map of {@code key -> [value, ...]} to the query string.
6658 *
67- * @param key Key of the parameter.
68- * @param values List of values
59+ * @param values Map of keys to lists of values.
6960 */
70- public void add (String key , List <String > values ) {
71- for (String value : values ) {
72- add (key , value );
61+ public void add (Map < String , List <String > > values ) {
62+ for (var entry : values . entrySet () ) {
63+ add (entry . getKey (), entry . getValue () );
7364 }
7465 }
7566
7667 /**
77- * Add a query string parameter and values to the query string .
78- * <p>
79- * The given key and values will be percent-encoded. If the values are already percent-encoded, it will be
80- * double percent-encoded .
68+ * Add a query string parameter and value from a {@code @httpQueryParams} member .
69+ *
70+ * <p>Skips the key if it was already added via {@link #add} so {@code @httpQuery}
71+ * members take precedence over {@code @httpQueryParams} entries with the same key .
8172 *
82- * @param values List of values
73+ * @param key Key of the parameter.
74+ * @param value Value of the parameter (or null).
8375 */
84- public void add (Map <String , List <String >> values ) {
85- for (var entry : values .entrySet ()) {
86- add (entry .getKey (), entry .getValue ());
76+ public void addForQueryParams (String key , String value ) {
77+ if (!httpQueryKeys .contains (key )) {
78+ append (key , value );
79+ }
80+ }
81+
82+ private void append (String key , String value ) {
83+ if (!empty ) {
84+ builder .append ('&' );
85+ } else {
86+ empty = false ;
87+ }
88+
89+ URLEncoding .encodeUnreserved (key , builder , false );
90+ builder .append ('=' );
91+ if (value != null ) {
92+ URLEncoding .encodeUnreserved (value , builder , false );
8793 }
8894 }
8995
9096 /**
9197 * Check if the query string is empty.
9298 *
93- * @return Returns true if empty .
99+ * @return Returns true if no parameters have been added .
94100 */
95101 public boolean isEmpty () {
96- return values . isEmpty () ;
102+ return empty ;
97103 }
98104
99105 /**
@@ -103,28 +109,15 @@ public boolean isEmpty() {
103109 */
104110 @ Override
105111 public String toString () {
106- StringBuilder result = new StringBuilder ();
107- write (result );
108- return result .toString ();
112+ return builder .toString ();
109113 }
110114
111115 /**
112- * Write the query string directly to a string builder.
116+ * Append the query string directly to a string builder.
113117 *
114118 * @param sink Where to write.
115119 */
116120 public void write (StringBuilder sink ) {
117- for (int i = 0 ; i < values .size (); i += 2 ) {
118- if (i > 0 ) {
119- sink .append ('&' );
120- }
121- encode (values .get (i ), sink );
122- sink .append ('=' );
123- encode (values .get (i + 1 ), sink );
124- }
125- }
126-
127- private void encode (String raw , StringBuilder builder ) {
128- URLEncoding .encodeUnreserved (raw , builder , false );
121+ sink .append (builder );
129122 }
130123}
0 commit comments