ConfigSectionGenerator implements ConfigMetadataVisitor<Object> { protected String typeLabel(Type type) { String typeName = type.getTypeName(); switch (typeName) { case "java.lang.Boolean": return "boolean"; case "java.lang.Integer": return "int"; case "java.lang.Byte": return "byte"; case "java.lang.Double": return "double"; case "java.lang.Float": return "float"; case "java.lang.Short": return "short"; case "java.lang.Long": return "long"; case "java.lang.String": return "String"; default: if (type instanceof Class) { Class<?> classType = (Class<?>) type; if (Map.class.isAssignableFrom(classType)) { return "Map"; } else if (Collection.class.isAssignableFrom(classType)) { return "List"; } } else if (type instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) type; StringBuilder out = new StringBuilder(typeLabel(parameterizedType.getRawType())); out.append("<"); Type[] args = parameterizedType.getActualTypeArguments(); if (args != null) { for (int i = 0; i < args.length; i++) { if (i > 0) { out.append(", "); } out.append(typeLabel(args[i])); } } out.append(">"); return out.toString(); } return typeName; } } ConfigSectionGenerator(ConsoleAppender out, Set<Type> seenMetadataTypes); @Override Object visitObjectMetadata(ConfigObjectMetadata metadata); @Override Object visitValueMetadata(ConfigValueMetadata metadata); @Override Object visitListMetadata(ConfigListMetadata metadata); @Override Object visitMapMetadata(ConfigMapMetadata metadata);  }
@Test public void testVisitObjectConfig_Inheritance() { ConfigObjectMetadata sub1 = ConfigObjectMetadata.builder() .type(Config3.class) .typeLabel("c3") .description("Subtype desc") .addProperty(ConfigValueMetadata.builder("p0").type(Boolean.class).build()) .addProperty(ConfigValueMetadata.builder("p1").type(String.class).build()) .build(); ConfigObjectMetadata sub2 = ConfigObjectMetadata.builder() .type(Config4.class) .typeLabel("c4") .addProperty(ConfigValueMetadata.builder("p2").type(Integer.TYPE).description("Designates an integer value").build()) .addProperty(ConfigValueMetadata.builder("p3").type(Bootique.class).build()) .build(); ConfigObjectMetadata m1Config = ConfigObjectMetadata .builder("m1root") .description("Root config of M1") .type(ConfigRoot1.class) .abstractType(true) .addSubConfig(sub1) .addSubConfig(sub2) .build(); assertLines(m1Config, "m1root:", " #", " # Root config of M1", " # Resolved as 'io.bootique.help.config.ConfigSectionGeneratorTest$ConfigRoot1'.", " #", "", " #", " # Type option: c3", " # Subtype desc", " # Resolved as 'io.bootique.help.config.ConfigSectionGeneratorTest$Config3'.", " #", "", " type: 'c3'", " p0: <true|false>", " p1: <string>", "", " #", " # Type option: c4", " # Resolved as 'io.bootique.help.config.ConfigSectionGeneratorTest$Config4'.", " #", "", " type: 'c4'", " # Designates an integer value", " p2: <int>", " # Resolved as 'io.bootique.Bootique'.", " p3: <value>" ); }
@Test public void testVisitMapConfig_ValueInheritance() throws NoSuchFieldException { Type genericMapType = ConfigRoot2.class.getField("mapOfRoot1").getGenericType(); ConfigObjectMetadata sub1 = ConfigObjectMetadata.builder() .type(Config3.class) .typeLabel("c3") .addProperty(ConfigValueMetadata.builder("p0").type(Boolean.class).build()) .addProperty(ConfigValueMetadata.builder("p1").type(String.class).build()) .build(); ConfigObjectMetadata sub2 = ConfigObjectMetadata.builder() .type(Config4.class) .typeLabel("c4") .addProperty(ConfigValueMetadata.builder("p2").type(Integer.TYPE).description("Designates an integer value").build()) .addProperty(ConfigValueMetadata.builder("p3").type(Bootique.class).build()) .build(); ConfigObjectMetadata m1Config = ConfigObjectMetadata .builder() .description("One config") .type(ConfigRoot1.class) .abstractType(true) .addSubConfig(sub1) .addSubConfig(sub2) .build(); ConfigMapMetadata mapMd = ConfigMapMetadata.builder("root") .description("Map of things") .type(genericMapType) .keysType(String.class) .valuesType(m1Config).build(); assertLines(mapMd, "root:", " #", " # Map of things", " # Resolved as 'Map<String, io.bootique.help.config.ConfigSectionGeneratorTest$ConfigRoot1>'.", " #", " <string>:", " #", " # One config", " # Resolved as 'io.bootique.help.config.ConfigSectionGeneratorTest$ConfigRoot1'.", " #", "", " #", " # Type option: c3", " # Resolved as 'io.bootique.help.config.ConfigSectionGeneratorTest$Config3'.", " #", "", " type: 'c3'", " p0: <true|false>", " p1: <string>", "", " #", " # Type option: c4", " # Resolved as 'io.bootique.help.config.ConfigSectionGeneratorTest$Config4'.", " #", "", " type: 'c4'", " # Designates an integer value", " p2: <int>", " # Resolved as 'io.bootique.Bootique'.", " p3: <value>" ); }
@Test public void testVisitMapConfig_ValueInheritance_AbstractProps() throws NoSuchFieldException { Type genericMapType = ConfigRoot2.class.getField("mapOfRoot1").getGenericType(); ConfigObjectMetadata sub1 = ConfigObjectMetadata.builder() .type(Config3.class) .typeLabel("c3") .addProperty(ConfigValueMetadata.builder("p0").type(Boolean.class).build()) .addProperty(ConfigValueMetadata.builder("p1").type(String.class).build()) .build(); ConfigObjectMetadata m1Config = ConfigObjectMetadata .builder() .description("One config") .type(ConfigRoot1.class) .abstractType(true) .addProperty(ConfigValueMetadata.builder("shouldNotBePrinted").type(Bootique.class).build()) .addSubConfig(sub1) .build(); ConfigMapMetadata mapMd = ConfigMapMetadata.builder("root") .description("Map of things") .type(genericMapType) .keysType(String.class) .valuesType(m1Config).build(); assertLines(mapMd, "root:", " #", " # Map of things", " # Resolved as 'Map<String, io.bootique.help.config.ConfigSectionGeneratorTest$ConfigRoot1>'.", " #", " <string>:", " #", " # One config", " # Resolved as 'io.bootique.help.config.ConfigSectionGeneratorTest$ConfigRoot1'.", " #", "", " #", " # Type option: c3", " # Resolved as 'io.bootique.help.config.ConfigSectionGeneratorTest$Config3'.", " #", "", " type: 'c3'", " p0: <true|false>", " p1: <string>" ); }
@Test public void testTypeLabel() throws NoSuchFieldException { ConfigSectionGenerator generator = new ConfigSectionGenerator(mock(ConsoleAppender.class), new HashSet<>()); assertEquals("int", generator.typeLabel(Integer.class)); assertEquals("int", generator.typeLabel(Integer.TYPE)); assertEquals("boolean", generator.typeLabel(Boolean.class)); assertEquals("boolean", generator.typeLabel(Boolean.TYPE)); assertEquals("String", generator.typeLabel(String.class)); assertEquals("io.bootique.Bootique", generator.typeLabel(Bootique.class)); assertEquals("Map", generator.typeLabel(HashMap.class)); assertEquals("List", generator.typeLabel(ArrayList.class)); Type genericMapType = ConfigRoot2.class.getField("map").getGenericType(); assertEquals("Map<int, String>", generator.typeLabel(genericMapType)); Type genericListType = ConfigRoot2.class.getField("list").getGenericType(); assertEquals("List<String>", generator.typeLabel(genericListType)); }