PipelineOptionsFactory { public static List<PipelineOptionDescriptor> describe( Set<Class<? extends PipelineOptions>> ifaces) { checkNotNull(ifaces); List<PipelineOptionDescriptor> result = new ArrayList<>(); Set<Method> seenMethods = Sets.newHashSet(); for (Class<? extends PipelineOptions> iface : ifaces) { CACHE.get().validateWellFormed(iface); Set<PipelineOptionSpec> properties = PipelineOptionsReflector.getOptionSpecs(iface, false); RowSortedTable<Class<?>, String, Method> ifacePropGetterTable = TreeBasedTable.create(ClassNameComparator.INSTANCE, Ordering.natural()); for (PipelineOptionSpec prop : properties) { ifacePropGetterTable.put( prop.getDefiningInterface(), prop.getName(), prop.getGetterMethod()); } for (Map.Entry<Class<?>, Map<String, Method>> ifaceToPropertyMap : ifacePropGetterTable.rowMap().entrySet()) { Class<?> currentIface = ifaceToPropertyMap.getKey(); Map<String, Method> propertyNamesToGetters = ifaceToPropertyMap.getValue(); List<@KeyFor("propertyNamesToGetters") String> lists = Lists.newArrayList(propertyNamesToGetters.keySet()); lists.sort(String.CASE_INSENSITIVE_ORDER); for (String propertyName : lists) { Method method = propertyNamesToGetters.get(propertyName); if (!seenMethods.add(method)) { continue; } Class<?> returnType = method.getReturnType(); PipelineOptionType.Enum optionType = PipelineOptionType.Enum.STRING; if (JSON_INTEGER_TYPES.contains(returnType)) { optionType = PipelineOptionType.Enum.INTEGER; } else if (JSON_NUMBER_TYPES.contains(returnType)) { optionType = PipelineOptionType.Enum.NUMBER; } else if (returnType == boolean.class || returnType == Boolean.class) { optionType = PipelineOptionType.Enum.BOOLEAN; } else if (List.class.isAssignableFrom(returnType)) { optionType = PipelineOptionType.Enum.ARRAY; } String optionName = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, propertyName); Description description = method.getAnnotation(Description.class); PipelineOptionDescriptor.Builder builder = PipelineOptionDescriptor.newBuilder() .setName(optionName) .setType(optionType) .setGroup(currentIface.getName()); Optional<String> defaultValue = getDefaultValueFromAnnotation(method); if (defaultValue.isPresent()) { builder.setDefaultValue(defaultValue.get()); } if (description != null) { builder.setDescription(description.value()); } result.add(builder.build()); } } } return result; }  static PipelineOptions create(); static T as(Class<T> klass); static Builder fromArgs(String... args); Builder withValidation(); static synchronized void register(Class<? extends PipelineOptions> iface); @Experimental static synchronized void resetCache(); static Set<Class<? extends PipelineOptions>> getRegisteredOptions(); static void printHelp(PrintStream out); static void printHelp(PrintStream out, Class<? extends PipelineOptions> iface); static List<PipelineOptionDescriptor> describe(
      Set<Class<? extends PipelineOptions>> ifaces);  }
@Test public void testDescribe() { List<PipelineOptionDescriptor> described = PipelineOptionsFactory.describe( Sets.newHashSet(PipelineOptions.class, TestDescribeOptions.class)); Map<String, PipelineOptionDescriptor> mapped = uniqueIndex(described, input -> input.getName()); assertEquals("no duplicates", described.size(), mapped.size()); Collection<PipelineOptionDescriptor> filtered = Collections2.filter( described, input -> input.getGroup().equals(TestDescribeOptions.class.getName())); assertEquals(6, filtered.size()); mapped = uniqueIndex(filtered, input -> input.getName()); PipelineOptionDescriptor listDesc = mapped.get("list"); assertThat(listDesc, notNullValue()); assertThat(listDesc.getDescription(), isEmptyString()); assertEquals(PipelineOptionType.Enum.ARRAY, listDesc.getType()); assertThat(listDesc.getDefaultValue(), isEmptyString()); PipelineOptionDescriptor stringDesc = mapped.get("string"); assertThat(stringDesc, notNullValue()); assertThat(stringDesc.getDescription(), isEmptyString()); assertEquals(PipelineOptionType.Enum.STRING, stringDesc.getType()); assertThat(stringDesc.getDefaultValue(), isEmptyString()); PipelineOptionDescriptor integerDesc = mapped.get("integer"); assertThat(integerDesc, notNullValue()); assertEquals("integer property", integerDesc.getDescription()); assertEquals(PipelineOptionType.Enum.INTEGER, integerDesc.getType()); assertThat(integerDesc.getDefaultValue(), isEmptyString()); PipelineOptionDescriptor floatDesc = mapped.get("float"); assertThat(integerDesc, notNullValue()); assertEquals("float number property", floatDesc.getDescription()); assertEquals(PipelineOptionType.Enum.NUMBER, floatDesc.getType()); assertThat(floatDesc.getDefaultValue(), isEmptyString()); PipelineOptionDescriptor booleanSimpleDesc = mapped.get("boolean_simple"); assertThat(booleanSimpleDesc, notNullValue()); assertEquals("simple boolean property", booleanSimpleDesc.getDescription()); assertEquals(PipelineOptionType.Enum.BOOLEAN, booleanSimpleDesc.getType()); assertThat(booleanSimpleDesc.getDefaultValue(), equalTo("true")); PipelineOptionDescriptor booleanWrapperDesc = mapped.get("boolean_wrapper"); assertThat(booleanWrapperDesc, notNullValue()); assertThat(booleanWrapperDesc.getDescription(), isEmptyString()); assertEquals(PipelineOptionType.Enum.BOOLEAN, booleanWrapperDesc.getType()); assertThat(booleanWrapperDesc.getDefaultValue(), equalTo("false")); }