in sdk/src/main/java/com/google/cloud/dataflow/sdk/options/PipelineOptionsFactory.java [1239:1336]
private static <T extends PipelineOptions> Map<String, Object> parseObjects(
Class<T> klass, ListMultimap<String, String> options, boolean strictParsing) {
Map<String, Method> propertyNamesToGetters = Maps.newHashMap();
PipelineOptionsFactory.validateWellFormed(klass, REGISTERED_OPTIONS);
@SuppressWarnings("unchecked")
Iterable<PropertyDescriptor> propertyDescriptors =
PipelineOptionsFactory.getPropertyDescriptors(
FluentIterable.from(getRegisteredOptions()).append(klass).toSet());
for (PropertyDescriptor descriptor : propertyDescriptors) {
propertyNamesToGetters.put(descriptor.getName(), descriptor.getReadMethod());
}
Map<String, Object> convertedOptions = Maps.newHashMap();
for (final Map.Entry<String, Collection<String>> entry : options.asMap().entrySet()) {
try {
// Search for close matches for missing properties.
// Either off by one or off by two character errors.
if (!propertyNamesToGetters.containsKey(entry.getKey())) {
SortedSet<String> closestMatches = new TreeSet<String>(
Sets.filter(propertyNamesToGetters.keySet(), new Predicate<String>() {
@Override
public boolean apply(@Nullable String input) {
return StringUtils.getLevenshteinDistance(entry.getKey(), input) <= 2;
}
}));
switch (closestMatches.size()) {
case 0:
throw new IllegalArgumentException(
String.format("Class %s missing a property named '%s'.",
klass, entry.getKey()));
case 1:
throw new IllegalArgumentException(
String.format("Class %s missing a property named '%s'. Did you mean '%s'?",
klass, entry.getKey(), Iterables.getOnlyElement(closestMatches)));
default:
throw new IllegalArgumentException(
String.format("Class %s missing a property named '%s'. Did you mean one of %s?",
klass, entry.getKey(), closestMatches));
}
}
Method method = propertyNamesToGetters.get(entry.getKey());
// Only allow empty argument values for String, String Array, and Collection.
Class<?> returnType = method.getReturnType();
JavaType type = MAPPER.getTypeFactory().constructType(method.getGenericReturnType());
if ("runner".equals(entry.getKey())) {
String runner = Iterables.getOnlyElement(entry.getValue());
Preconditions.checkArgument(SUPPORTED_PIPELINE_RUNNERS.containsKey(runner),
"Unknown 'runner' specified '%s', supported pipeline runners %s",
runner, Sets.newTreeSet(SUPPORTED_PIPELINE_RUNNERS.keySet()));
convertedOptions.put("runner", SUPPORTED_PIPELINE_RUNNERS.get(runner));
} else if ((returnType.isArray() && (SIMPLE_TYPES.contains(returnType.getComponentType())
|| returnType.getComponentType().isEnum()))
|| Collection.class.isAssignableFrom(returnType)) {
// Split any strings with ","
List<String> values = FluentIterable.from(entry.getValue())
.transformAndConcat(new Function<String, Iterable<String>>() {
@Override
public Iterable<String> apply(String input) {
return Arrays.asList(input.split(","));
}
}).toList();
if (returnType.isArray() && !returnType.getComponentType().equals(String.class)) {
for (String value : values) {
Preconditions.checkArgument(!value.isEmpty(),
"Empty argument value is only allowed for String, String Array, and Collection,"
+ " but received: " + returnType);
}
}
convertedOptions.put(entry.getKey(), MAPPER.convertValue(values, type));
} else if (SIMPLE_TYPES.contains(returnType) || returnType.isEnum()) {
String value = Iterables.getOnlyElement(entry.getValue());
Preconditions.checkArgument(returnType.equals(String.class) || !value.isEmpty(),
"Empty argument value is only allowed for String, String Array, and Collection,"
+ " but received: " + returnType);
convertedOptions.put(entry.getKey(), MAPPER.convertValue(value, type));
} else {
String value = Iterables.getOnlyElement(entry.getValue());
Preconditions.checkArgument(returnType.equals(String.class) || !value.isEmpty(),
"Empty argument value is only allowed for String, String Array, and Collection,"
+ " but received: " + returnType);
try {
convertedOptions.put(entry.getKey(), MAPPER.readValue(value, type));
} catch (IOException e) {
throw new IllegalArgumentException("Unable to parse JSON value " + value, e);
}
}
} catch (IllegalArgumentException e) {
if (strictParsing) {
throw e;
} else {
LOG.warn("Strict parsing is disabled, ignoring option '{}' with value '{}' because {}",
entry.getKey(), entry.getValue(), e.getMessage());
}
}
}
return convertedOptions;
}