| 
19 | 19 | import com.google.api.client.util.Preconditions;  | 
20 | 20 | import com.google.api.client.util.Types;  | 
21 | 21 | import com.google.api.client.util.escape.CharEscapers;  | 
 | 22 | +import com.google.common.base.Splitter;  | 
 | 23 | +import com.google.common.collect.ImmutableList;  | 
22 | 24 | 
 
  | 
23 | 25 | import java.util.HashMap;  | 
24 | 26 | import java.util.Iterator;  | 
25 | 27 | import java.util.LinkedHashMap;  | 
 | 28 | +import java.util.ListIterator;  | 
26 | 29 | import java.util.Map;  | 
27 | 30 | 
 
  | 
28 | 31 | /**  | 
 | 
37 | 40 |  * keys := [("semi", ";"),("dot", "."),("comma", ",")]  | 
38 | 41 |  *  | 
39 | 42 |  * The following templates results in the following expansions:  | 
40 |  | - * {var} -> value  | 
41 |  | - * {list} -> red,green,blue  | 
42 |  | - * {list*} -> red,green,blue  | 
43 |  | - * {keys} -> semi,%3B,dot,.,comma,%2C  | 
44 |  | - * {keys*} -> semi=%3B,dot=.,comma=%2C  | 
45 |  | - * {+list} -> red,green,blue  | 
46 |  | - * {+list*} -> red,green,blue  | 
47 |  | - * {+keys} -> semi,;,dot,.,comma,,  | 
48 |  | - * {+keys*} -> semi=;,dot=.,comma=,  | 
49 |  | - * {#list} -> #red,green,blue  | 
50 |  | - * {#list*} -> #red,green,blue  | 
51 |  | - * {#keys} -> #semi,;,dot,.,comma,,  | 
52 |  | - * {#keys*} -> #semi=;,dot=.,comma=,  | 
53 |  | - * X{.list} -> X.red,green,blue  | 
54 |  | - * X{.list*} -> X.red.green.blue  | 
55 |  | - * X{.keys} -> X.semi,%3B,dot,.,comma,%2C  | 
56 |  | - * X{.keys*} -> X.semi=%3B.dot=..comma=%2C  | 
57 |  | - * {/list} -> /red,green,blue  | 
58 |  | - * {/list*} -> /red/green/blue  | 
59 |  | - * {/keys} -> /semi,%3B,dot,.,comma,%2C  | 
60 |  | - * {/keys*} -> /semi=%3B/dot=./comma=%2C  | 
61 |  | - * {;list} -> ;list=red,green,blue  | 
62 |  | - * {;list*} -> ;list=red;list=green;list=blue  | 
63 |  | - * {;keys} -> ;keys=semi,%3B,dot,.,comma,%2C  | 
64 |  | - * {;keys*} -> ;semi=%3B;dot=.;comma=%2C  | 
65 |  | - * {?list} -> ?list=red,green,blue  | 
66 |  | - * {?list*} -> ?list=red&list=green&list=blue  | 
67 |  | - * {?keys} -> ?keys=semi,%3B,dot,.,comma,%2C  | 
68 |  | - * {?keys*} -> ?semi=%3B&dot=.&comma=%2C  | 
69 |  | - * {&list} -> &list=red,green,blue  | 
70 |  | - * {&list*} -> &list=red&list=green&list=blue  | 
71 |  | - * {&keys} -> &keys=semi,%3B,dot,.,comma,%2C  | 
72 |  | - * {&keys*} -> &semi=%3B&dot=.&comma=%2C  | 
 | 43 | + * {var} -> value  | 
 | 44 | + * {list} -> red,green,blue  | 
 | 45 | + * {list*} -> red,green,blue  | 
 | 46 | + * {keys} -> semi,%3B,dot,.,comma,%2C  | 
 | 47 | + * {keys*} -> semi=%3B,dot=.,comma=%2C  | 
 | 48 | + * {+list} -> red,green,blue  | 
 | 49 | + * {+list*} -> red,green,blue  | 
 | 50 | + * {+keys} -> semi,;,dot,.,comma,,  | 
 | 51 | + * {+keys*} -> semi=;,dot=.,comma=,  | 
 | 52 | + * {#list} -> #red,green,blue  | 
 | 53 | + * {#list*} -> #red,green,blue  | 
 | 54 | + * {#keys} -> #semi,;,dot,.,comma,,  | 
 | 55 | + * {#keys*} -> #semi=;,dot=.,comma=,  | 
 | 56 | + * X{.list} -> X.red,green,blue  | 
 | 57 | + * X{.list*} -> X.red.green.blue  | 
 | 58 | + * X{.keys} -> X.semi,%3B,dot,.,comma,%2C  | 
 | 59 | + * X{.keys*} -> X.semi=%3B.dot=..comma=%2C  | 
 | 60 | + * {/list} -> /red,green,blue  | 
 | 61 | + * {/list*} -> /red/green/blue  | 
 | 62 | + * {/keys} -> /semi,%3B,dot,.,comma,%2C  | 
 | 63 | + * {/keys*} -> /semi=%3B/dot=./comma=%2C  | 
 | 64 | + * {;list} -> ;list=red,green,blue  | 
 | 65 | + * {;list*} -> ;list=red;list=green;list=blue  | 
 | 66 | + * {;keys} -> ;keys=semi,%3B,dot,.,comma,%2C  | 
 | 67 | + * {;keys*} -> ;semi=%3B;dot=.;comma=%2C  | 
 | 68 | + * {?list} -> ?list=red,green,blue  | 
 | 69 | + * {?list*} -> ?list=red&list=green&list=blue  | 
 | 70 | + * {?keys} -> ?keys=semi,%3B,dot,.,comma,%2C  | 
 | 71 | + * {?keys*} -> ?semi=%3B&dot=.&comma=%2C  | 
 | 72 | + * {&list} -> &list=red,green,blue  | 
 | 73 | + * {&list*} -> &list=red&list=green&list=blue  | 
 | 74 | + * {&keys} -> &keys=semi,%3B,dot,.,comma,%2C  | 
 | 75 | + * {&keys*} -> &semi=%3B&dot=.&comma=%2C  | 
 | 76 | + * {?var,list} -> ?var=value&list=red,green,blue  | 
73 | 77 |  *  | 
74 | 78 |  * @since 1.6  | 
75 | 79 |  * @author Ravi Mistry  | 
@@ -294,52 +298,71 @@ public static String expand(String pathUri, Object parameters,  | 
294 | 298 |  }  | 
295 | 299 |  pathBuf.append(pathUri.substring(cur, next));  | 
296 | 300 |  int close = pathUri.indexOf('}', next + 2);  | 
297 |  | - String template = pathUri.substring(next + 1, close);  | 
298 | 301 |  cur = close + 1;  | 
299 | 302 | 
 
  | 
300 |  | - boolean containsExplodeModifier = template.endsWith("*");  | 
301 |  | - CompositeOutput compositeOutput = getCompositeOutput(template);  | 
302 |  | - | 
303 |  | - int varNameStartIndex = compositeOutput.getVarNameStartIndex();  | 
304 |  | - int varNameEndIndex = template.length();  | 
305 |  | - if (containsExplodeModifier) {  | 
306 |  | - // The expression contains an explode modifier '*' at the end, update end index.  | 
307 |  | - varNameEndIndex = varNameEndIndex - 1;  | 
308 |  | - }  | 
309 |  | - // Now get varName devoid of any prefixes and explode modifiers.  | 
310 |  | - String varName = template.substring(varNameStartIndex, varNameEndIndex);  | 
 | 303 | + String templates = pathUri.substring(next + 1, close);  | 
 | 304 | + CompositeOutput compositeOutput = getCompositeOutput(templates);  | 
 | 305 | + ListIterator<String> templateIterator =  | 
 | 306 | + ImmutableList.copyOf(Splitter.on(',').split(templates)).listIterator();  | 
 | 307 | + boolean isFirstParameter = true;  | 
 | 308 | + while (templateIterator.hasNext()) {  | 
 | 309 | + String template = templateIterator.next();  | 
 | 310 | + boolean containsExplodeModifier = template.endsWith("*");  | 
 | 311 | + | 
 | 312 | + int varNameStartIndex = templateIterator.nextIndex() == 1  | 
 | 313 | + ? compositeOutput.getVarNameStartIndex() : 0;  | 
 | 314 | + int varNameEndIndex = template.length();  | 
 | 315 | + if (containsExplodeModifier) {  | 
 | 316 | + // The expression contains an explode modifier '*' at the end, update end index.  | 
 | 317 | + varNameEndIndex = varNameEndIndex - 1;  | 
 | 318 | + }  | 
 | 319 | + // Now get varName devoid of any prefixes and explode modifiers.  | 
 | 320 | + String varName = template.substring(varNameStartIndex, varNameEndIndex);  | 
311 | 321 | 
 
  | 
312 |  | - Object value = variableMap.remove(varName);  | 
313 |  | - if (value == null) {  | 
314 |  | - // The value for this variable is undefined. continue with the next template.  | 
315 |  | - continue;  | 
316 |  | - }  | 
317 |  | - if (value instanceof Iterator<?>) {  | 
318 |  | - // Get the list property value.  | 
319 |  | - Iterator<?> iterator = (Iterator<?>) value;  | 
320 |  | - value = getListPropertyValue(varName, iterator, containsExplodeModifier, compositeOutput);  | 
321 |  | - } else if (value instanceof Iterable<?> || value.getClass().isArray()) {  | 
322 |  | - // Get the list property value.  | 
323 |  | - Iterator<?> iterator = Types.iterableOf(value).iterator();  | 
324 |  | - value = getListPropertyValue(varName, iterator, containsExplodeModifier, compositeOutput);  | 
325 |  | - } else if (value.getClass().isEnum()) {  | 
326 |  | - String name = FieldInfo.of((Enum<?>) value).getName();  | 
327 |  | - if (name != null) {  | 
328 |  | - value = CharEscapers.escapeUriPath(name);  | 
 | 322 | + Object value = variableMap.remove(varName);  | 
 | 323 | + if (value == null) {  | 
 | 324 | + // The value for this variable is undefined. continue with the next template.  | 
 | 325 | + continue;  | 
329 | 326 |  }  | 
330 |  | - } else if (!Data.isValueOfPrimitiveType(value)) {  | 
331 |  | - // Parse the value as a key/value map.  | 
332 |  | - Map<String, Object> map = getMap(value);  | 
333 |  | - value = getMapPropertyValue(varName, map, containsExplodeModifier, compositeOutput);  | 
334 |  | - } else {  | 
335 |  | - // For everything else...  | 
336 |  | - if (compositeOutput.getReservedExpansion()) {  | 
337 |  | - value = CharEscapers.escapeUriPathWithoutReserved(value.toString());  | 
 | 327 | + if (!isFirstParameter) {  | 
 | 328 | + pathBuf.append(compositeOutput.getExplodeJoiner());  | 
 | 329 | + } else {  | 
 | 330 | + pathBuf.append(compositeOutput.getOutputPrefix());  | 
 | 331 | + isFirstParameter = false;  | 
 | 332 | + }  | 
 | 333 | + if (value instanceof Iterator<?>) {  | 
 | 334 | + // Get the list property value.  | 
 | 335 | + Iterator<?> iterator = (Iterator<?>) value;  | 
 | 336 | + value = getListPropertyValue(varName, iterator, containsExplodeModifier, compositeOutput);  | 
 | 337 | + } else if (value instanceof Iterable<?> || value.getClass().isArray()) {  | 
 | 338 | + // Get the list property value.  | 
 | 339 | + Iterator<?> iterator = Types.iterableOf(value).iterator();  | 
 | 340 | + value = getListPropertyValue(varName, iterator, containsExplodeModifier, compositeOutput);  | 
 | 341 | + } else if (value.getClass().isEnum()) {  | 
 | 342 | + String name = FieldInfo.of((Enum<?>) value).getName();  | 
 | 343 | + if (name != null) {  | 
 | 344 | + if (compositeOutput.requiresVarAssignment()) {  | 
 | 345 | + value = String.format("%s=%s", varName, value);  | 
 | 346 | + }  | 
 | 347 | + value = CharEscapers.escapeUriPath(value.toString());  | 
 | 348 | + }  | 
 | 349 | + } else if (!Data.isValueOfPrimitiveType(value)) {  | 
 | 350 | + // Parse the value as a key/value map.  | 
 | 351 | + Map<String, Object> map = getMap(value);  | 
 | 352 | + value = getMapPropertyValue(varName, map, containsExplodeModifier, compositeOutput);  | 
338 | 353 |  } else {  | 
339 |  | - value = CharEscapers.escapeUriPath(value.toString());  | 
 | 354 | + // For everything else...  | 
 | 355 | + if (compositeOutput.requiresVarAssignment()) {  | 
 | 356 | + value = String.format("%s=%s", varName, value);  | 
 | 357 | + }  | 
 | 358 | + if (compositeOutput.getReservedExpansion()) {  | 
 | 359 | + value = CharEscapers.escapeUriPathWithoutReserved(value.toString());  | 
 | 360 | + } else {  | 
 | 361 | + value = CharEscapers.escapeUriPath(value.toString());  | 
 | 362 | + }  | 
340 | 363 |  }  | 
 | 364 | + pathBuf.append(value);  | 
341 | 365 |  }  | 
342 |  | - pathBuf.append(value);  | 
343 | 366 |  }  | 
344 | 367 |  if (addUnusedParamsAsQueryParams) {  | 
345 | 368 |  // Add the parameters remaining in the variableMap as query parameters.  | 
@@ -367,7 +390,6 @@ private static String getListPropertyValue(String varName, Iterator<?> iterator,  | 
367 | 390 |  return "";  | 
368 | 391 |  }  | 
369 | 392 |  StringBuilder retBuf = new StringBuilder();  | 
370 |  | - retBuf.append(compositeOutput.getOutputPrefix());  | 
371 | 393 |  String joiner;  | 
372 | 394 |  if (containsExplodeModifier) {  | 
373 | 395 |  joiner = compositeOutput.getExplodeJoiner();  | 
@@ -410,7 +432,6 @@ private static String getMapPropertyValue(String varName, Map<String, Object> ma  | 
410 | 432 |  return "";  | 
411 | 433 |  }  | 
412 | 434 |  StringBuilder retBuf = new StringBuilder();  | 
413 |  | - retBuf.append(compositeOutput.getOutputPrefix());  | 
414 | 435 |  String joiner;  | 
415 | 436 |  String mapElementsJoiner;  | 
416 | 437 |  if (containsExplodeModifier) {  | 
 | 
0 commit comments