private static Map parseObjects()

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;
  }