FieldDocumentationGenerator { public FieldDescriptors generateDocumentation(Type baseType, TypeFactory typeFactory) throws JsonMappingException { JavaType javaBaseType = typeFactory.constructType(baseType); List<JavaType> types = resolveAllTypes(javaBaseType, typeFactory, typeMapping); FieldDescriptors result = new FieldDescriptors(); FieldDocumentationVisitorWrapper visitorWrapper = FieldDocumentationVisitorWrapper.create( javadocReader, constraintReader, deserializationConfig, new TypeRegistry(typeMapping, types), typeFactory, translationResolver, skipAccessor); for (JavaType type : types) { log.debug("(TOP) {}", type.getRawClass().getSimpleName()); if (RESOURCES_TYPES.contains(type.getRawClass().getCanonicalName())) { result.setNoContentMessageKey("body-as-embedded-resources"); continue; } writer.acceptJsonFormatVisitor(type, visitorWrapper); } for (FieldDescriptor descriptor : visitorWrapper.getFields()) { result.putIfAbsent(descriptor.getPath(), descriptor); } return result; } FieldDocumentationGenerator(
            ObjectWriter writer,
            DeserializationConfig deserializationConfig,
            JavadocReader javadocReader,
            ConstraintReader constraintReader,
            TypeMapping typeMapping,
            SnippetTranslationResolver translationResolver,
            JsonProperty.Access skipAccessor
    ); FieldDescriptors generateDocumentation(Type baseType, TypeFactory typeFactory);  }
@Test public void testGenerateDocumentationForBasicTypes() throws Exception { ObjectMapper mapper = createMapper(); mockFieldComment(BasicTypes.class, "string", "A string"); mockFieldComment(BasicTypes.class, "bool", "A boolean"); mockFieldComment(BasicTypes.class, "number", "An integer"); mockFieldComment(BasicTypes.class, "decimal", "A decimal"); FieldDocumentationGenerator generator = new FieldDocumentationGenerator(mapper.writer(), mapper.getDeserializationConfig(), javadocReader, constraintReader, typeMapping, SnippetTranslationManager.getDefaultResolver(), null); Type type = BasicTypes.class; List<ExtendedFieldDescriptor> result = cast(generator .generateDocumentation(type, mapper.getTypeFactory()).values()); assertThat(result.size(), is(4)); assertThat(result.get(0), is(descriptor("string", "String", "A string", "true"))); assertThat(result.get(1), is(descriptor("bool", "Boolean", "A boolean", "true"))); assertThat(result.get(2), is(descriptor("number", "Integer", "An integer", "true"))); assertThat(result.get(3), is(descriptor("decimal", "Decimal", "A decimal", "true"))); }
@Test public void testGenerateDocumentationForPrimitiveTypes() throws Exception { ObjectMapper mapper = createMapper(); mockFieldComment(PrimitiveTypes.class, "string", "A string"); mockFieldComment(PrimitiveTypes.class, "bool", "A boolean"); mockFieldComment(PrimitiveTypes.class, "number", "An integer"); mockFieldComment(PrimitiveTypes.class, "decimal", "A decimal"); FieldDocumentationGenerator generator = new FieldDocumentationGenerator(mapper.writer(), mapper.getDeserializationConfig(), javadocReader, constraintReader, typeMapping, SnippetTranslationManager.getDefaultResolver(), null); Type type = PrimitiveTypes.class; List<ExtendedFieldDescriptor> result = cast(generator .generateDocumentation(type, mapper.getTypeFactory()).values()); assertThat(result.size(), is(4)); assertThat(result.get(0), is(descriptor("string", "Array[String]", "A string", "true"))); assertThat(result.get(1), is(descriptor("bool", "Boolean", "A boolean", "true"))); assertThat(result.get(2), is(descriptor("number", "Integer", "An integer", "true"))); assertThat(result.get(3), is(descriptor("decimal", "Decimal", "A decimal", "true"))); mapper.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, true); generator = new FieldDocumentationGenerator(mapper.writer(), mapper.getDeserializationConfig(), javadocReader, constraintReader, typeMapping, SnippetTranslationManager.getDefaultResolver(), null); result = cast(generator.generateDocumentation(type, mapper.getTypeFactory()).values()); assertThat(result.size(), is(4)); assertThat(result.get(0), is(descriptor("string", "Array[String]", "A string", "true"))); assertThat(result.get(1), is(descriptor("bool", "Boolean", "A boolean", "false"))); assertThat(result.get(2), is(descriptor("number", "Integer", "An integer", "false"))); assertThat(result.get(3), is(descriptor("decimal", "Decimal", "A decimal", "false"))); }
@Test public void testGenerateDocumentationForComposedTypes() throws Exception { ObjectMapper mapper = createMapper(); mockFieldComment(ComposedTypes.class, "object", "An object"); mockFieldComment(BasicTypes.class, "string", "A string"); mockFieldComment(BasicTypes.class, "bool", "A boolean"); mockFieldComment(BasicTypes.class, "number", "An integer"); mockFieldComment(BasicTypes.class, "decimal", "A decimal"); mockFieldComment(ComposedTypes.class, "array", "An array"); FieldDocumentationGenerator generator = new FieldDocumentationGenerator(mapper.writer(), mapper.getDeserializationConfig(), javadocReader, constraintReader, typeMapping, SnippetTranslationManager.getDefaultResolver(), null); Type type = ComposedTypes.class; List<ExtendedFieldDescriptor> result = cast(generator .generateDocumentation(type, mapper.getTypeFactory()).values()); assertThat(result.size(), is(10)); assertThat(result.get(0), is(descriptor("object", "Object", "An object", "true"))); assertThat(result.get(1), is(descriptor("object.string", "String", "A string", "true"))); assertThat(result.get(2), is(descriptor("object.bool", "Boolean", "A boolean", "true"))); assertThat(result.get(3), is(descriptor("object.number", "Integer", "An integer", "true"))); assertThat(result.get(4), is(descriptor("object.decimal", "Decimal", "A decimal", "true"))); assertThat(result.get(5), is(descriptor("array", "Array[Object]", "An array", "true"))); assertThat(result.get(6), is(descriptor("array[].string", "String", "A string", "true"))); assertThat(result.get(7), is(descriptor("array[].bool", "Boolean", "A boolean", "true"))); assertThat(result.get(8), is(descriptor("array[].number", "Integer", "An integer", "true"))); assertThat(result.get(9), is(descriptor("array[].decimal", "Decimal", "A decimal", "true"))); }
@Test public void testGenerateDocumentationForNestedTypes() throws Exception { ObjectMapper mapper = createMapper(); mockFieldComment(FirstLevel.class, "second", "2nd level"); mockFieldComment(SecondLevel.class, "third", "3rd level"); mockFieldComment(ThirdLevel.class, "fourth", "4th level"); mockFieldComment(FourthLevel.class, "fifth", "5th level"); mockFieldComment(FifthLevel.class, "last", "An integer"); FieldDocumentationGenerator generator = new FieldDocumentationGenerator(mapper.writer(), mapper.getDeserializationConfig(), javadocReader, constraintReader, typeMapping, SnippetTranslationManager.getDefaultResolver(), null); Type type = FirstLevel.class; List<ExtendedFieldDescriptor> result = cast(generator .generateDocumentation(type, mapper.getTypeFactory()).values()); assertThat(result.size(), is(5)); assertThat(result.get(0), is(descriptor("second", "Object", "2nd level", "true"))); assertThat(result.get(1), is(descriptor("second.third", "Array[Object]", "3rd level", "true"))); assertThat(result.get(2), is(descriptor("second.third[].fourth", "Object", "4th level", "true"))); assertThat(result.get(3), is(descriptor("second.third[].fourth.fifth", "Array[Object]", "5th level", "true"))); assertThat(result.get(4), is(descriptor("second.third[].fourth.fifth[].last", "Integer", "An integer", "true"))); }
@Test public void testGenerateDocumentationForRecursiveTypes() throws Exception { ObjectMapper mapper = createMapper(); FieldDocumentationGenerator generator = new FieldDocumentationGenerator(mapper.writer(), mapper.getDeserializationConfig(), javadocReader, constraintReader, typeMapping, SnippetTranslationManager.getDefaultResolver(), null); Type type = RecursiveType.class; List<ExtendedFieldDescriptor> result = cast(generator .generateDocumentation(type, mapper.getTypeFactory()).values()); assertThat(result.size(), is(16)); assertThat(result.get(0), is(descriptor("sub1", "Array[Object]", "", "true"))); assertThat(result.get(1), is(descriptor("sub2", "Object", "", "true"))); assertThat(result.get(2), is(descriptor("sub3", "Array[Object]", "", "true"))); assertThat(result.get(3), is(descriptor("sub4", "Object", "", "true"))); assertThat(result.get(4), is(descriptor("sub5", "Array[Object]", "", "true"))); assertThat(result.get(5), is(descriptor("sub6", "Object", "", "true"))); assertThat(result.get(6), is(descriptor("sub7", "Array[Object]", "", "true"))); assertThat(result.get(7), is(descriptor("sub7[].sub1", "Array[Object]", "", "true"))); assertThat(result.get(8), is(descriptor("sub7[].sub2", "Object", "", "true"))); assertThat(result.get(9), is(descriptor("sub7[].sub3", "Array[Object]", "", "true"))); assertThat(result.get(10), is(descriptor("sub7[].sub4", "Object", "", "true"))); assertThat(result.get(11), is(descriptor("sub8", "Object", "", "true"))); assertThat(result.get(12), is(descriptor("sub8.sub1", "Array[Object]", "", "true"))); assertThat(result.get(13), is(descriptor("sub8.sub2", "Object", "", "true"))); assertThat(result.get(14), is(descriptor("sub8.sub3", "Array[Object]", "", "true"))); assertThat(result.get(15), is(descriptor("sub8.sub4", "Object", "", "true"))); }
@Test public void testGenerateDocumentationForExternalSerializer() throws Exception { ObjectMapper mapper = createMapper(); mockFieldComment(ExternalSerializer.class, "bigDecimal", "A decimal"); SimpleModule testModule = new SimpleModule("TestModule"); testModule.addSerializer(new BigDecimalSerializer()); mapper.registerModule(testModule); FieldDocumentationGenerator generator = new FieldDocumentationGenerator(mapper.writer(), mapper.getDeserializationConfig(), javadocReader, constraintReader, typeMapping, SnippetTranslationManager.getDefaultResolver(), null); Type type = ExternalSerializer.class; List<ExtendedFieldDescriptor> result = cast(generator .generateDocumentation(type, mapper.getTypeFactory()).values()); assertThat(result.size(), is(1)); assertThat(result.get(0), is(descriptor("bigDecimal", "Decimal", "A decimal", "true"))); }
@Test public void testGenerateDocumentationForJacksonAnnotations() throws Exception { ObjectMapper mapper = createMapper(); mockFieldComment(JsonAnnotations.class, "location", "A location"); mockFieldComment(JsonAnnotations.class, "uri", "A uri"); when(javadocReader.resolveMethodComment(JsonAnnotations.class, "getParameter")) .thenReturn("A parameter"); mockFieldComment(JsonAnnotations.Meta.class, "headers", "A header map"); FieldDocumentationGenerator generator = new FieldDocumentationGenerator(mapper.writer(), mapper.getDeserializationConfig(), javadocReader, constraintReader, typeMapping, SnippetTranslationManager.getDefaultResolver(), null); Type type = JsonAnnotations.class; List<ExtendedFieldDescriptor> result = cast(generator .generateDocumentation(type, mapper.getTypeFactory()).values()); assertThat(result.size(), is(4)); assertThat(result.get(0), is(descriptor("uri", "String", "A uri", "true"))); assertThat(result.get(1), is(descriptor("path", "String", "A location", "true"))); assertThat(result.get(2), is(descriptor("param", "String", "A parameter", "true"))); assertThat(result.get(3), is(descriptor("headers", "Map", "A header map", "true"))); }
@Test public void testGenerateDocumentationForFieldResolution() throws Exception { ObjectMapper mapper = new ObjectMapper(); mapper.setVisibility(mapper.getSerializationConfig().getDefaultVisibilityChecker() .withFieldVisibility(JsonAutoDetect.Visibility.PUBLIC_ONLY)); mockFieldComment(FieldCommentResolution.class, "location", "A location"); when(javadocReader.resolveMethodComment(FieldCommentResolution.class, "getType")) .thenReturn("A type"); mockFieldComment(FieldCommentResolution.class, "uri", "A uri"); mockFieldComment(FieldCommentResolution.class, "secured", "A secured flag"); FieldDocumentationGenerator generator = new FieldDocumentationGenerator(mapper.writer(), mapper.getDeserializationConfig(), javadocReader, constraintReader, typeMapping, SnippetTranslationManager.getDefaultResolver(), null); Type type = FieldCommentResolution.class; List<ExtendedFieldDescriptor> result = cast(generator .generateDocumentation(type, mapper.getTypeFactory()).values()); assertThat(result.size(), is(4)); assertThat(result.get(0), is(descriptor("location", "String", "A location", "true"))); assertThat(result.get(1), is(descriptor("type", "String", "A type", "true"))); assertThat(result.get(2), is(descriptor("uri", "String", "A uri", "true"))); assertThat(result.get(3), is(descriptor("secured", "Boolean", "A secured flag", "true"))); }
@Test public void testGenerateDocumentationForConstraints() throws Exception { ObjectMapper mapper = createMapper(); mockConstraint(ConstraintResolution.class, "location", "A constraint for location"); mockConstraint(ConstraintResolution.class, "type", "A constraint for type"); mockOptional(ConstraintResolution.class, "type", "false"); mockOptional(ConstraintResolution.class, "params", "false"); mockConstraint(ConstraintField.class, "value", "A constraint1 for value", "A constraint2 for value"); mockOptional(ConstraintField.class, "value", "false"); FieldDocumentationGenerator generator = new FieldDocumentationGenerator(mapper.writer(), mapper.getDeserializationConfig(), javadocReader, constraintReader, typeMapping, SnippetTranslationManager.getDefaultResolver(), null); Type type = ConstraintResolution.class; List<ExtendedFieldDescriptor> fieldDescriptions = cast(generator .generateDocumentation(type, mapper.getTypeFactory()).values()); assertThat(fieldDescriptions.size(), is(5)); assertThat(fieldDescriptions.get(0), is(descriptor("location", "String", "", "true", "A constraint for location"))); assertThat(fieldDescriptions.get(1), is(descriptor("type", "Integer", "", "false", "A constraint for type"))); assertThat(fieldDescriptions.get(2), is(descriptor("params", "Array[Object]", "", "false"))); assertThat(fieldDescriptions.get(3), is(descriptor("params[].value", "String", "", "false", "A constraint1 for value", "A constraint2 for value"))); assertThat(fieldDescriptions.get(4), is(descriptor("flags", "Array[Boolean]", "", "true"))); }
@Test public void testGenerateDocumentationForJacksonSubTypes() throws Exception { ObjectMapper mapper = createMapper(); mockFieldComment(JsonType1.class, "name", "A name"); mockFieldComment(JsonType1.class, "type", "A type"); mockFieldComment(JsonType1.class, "base1", "A base 1"); mockFieldComment(JsonType1.class, "base2", "A base 2"); mockFieldComment(JsonType1.class, "base3", "A base 3"); mockFieldComment(JsonType1.class, "base4", "A base 4"); mockFieldComment(JsonType1SubType1.class, "base1Sub1", "A base 1 sub 1"); mockFieldComment(JsonType1SubType2.class, "base1Sub2", "A base 1 sub 2"); mockFieldComment(JsonType2.class, "clazz", "A clazz"); mockFieldComment(JsonType2SubType1.class, "base2Sub1", "A base 2 sub 1"); mockFieldComment(JsonType2SubType2.class, "base2Sub2", "A base 2 sub 2"); mockTypeSpecifier(JsonType1SubType1.class, "[S1]"); FieldDocumentationGenerator generator = new FieldDocumentationGenerator(mapper.writer(), mapper.getDeserializationConfig(), javadocReader, constraintReader, typeMapping, SnippetTranslationManager.getDefaultResolver(), null); Type type = JsonType1.class; List<ExtendedFieldDescriptor> fieldDescriptions = cast(generator .generateDocumentation(type, mapper.getTypeFactory()).values()); assertThat(fieldDescriptions.size(), is(14)); assertThat(fieldDescriptions.get(0), is(descriptor("type", "String", "A type", "true"))); assertThat(fieldDescriptions.get(1), is(descriptor("name", "String", "A name", "true"))); assertThat(fieldDescriptions.get(2), is(descriptor("base1", "Array[Object]", "A base 1", "true"))); assertThat(fieldDescriptions.get(3), is(descriptor("base2", "Object", "A base 2", "true"))); assertThat(fieldDescriptions.get(4), is(descriptor("base3", "Array[Object]", "A base 3", "true"))); assertThat(fieldDescriptions.get(5), is(descriptor("base3[].clazz", "String", "A clazz", "true"))); assertThat(fieldDescriptions.get(6), is(descriptor("base3[].base2Sub1", "String", "A base 2 sub 1", "true"))); assertThat(fieldDescriptions.get(7), is(descriptor("base3[].base2Sub2", "String", "A base 2 sub 2", "true"))); assertThat(fieldDescriptions.get(8), is(descriptor("base4", "Object", "A base 4", "true"))); assertThat(fieldDescriptions.get(9), is(descriptor("base4.clazz", "String", "A clazz", "true"))); assertThat(fieldDescriptions.get(10), is(descriptor("base4.base2Sub1", "String", "A base 2 sub 1", "true"))); assertThat(fieldDescriptions.get(11), is(descriptor("base4.base2Sub2", "String", "A base 2 sub 2", "true"))); assertThat(fieldDescriptions.get(12), is(descriptor("base1Sub1", "String", "A base 1 sub 1 [S1]", "true [S1]"))); assertThat(fieldDescriptions.get(13), is(descriptor("base1Sub2", "String", "A base 1 sub 2", "true"))); }
@Test public void testGenerateDocumentationForPlainSubTypes() throws Exception { ObjectMapper mapper = createMapper(); mockFieldComment(Plain.class, "field", "A field"); mockFieldComment(Plain.class, "overriddenField", "An overridden field"); mockFieldComment(PlainX.class, "overriddenField", "Custom 1"); mockFieldComment(PlainX.class, "x", "A field X"); mockFieldComment(PlainY.class, "overriddenField", "Custom 2"); mockFieldComment(PlainY.class, "y", "A field Y"); mockTypeSpecifier(Plain.class, ""); mockTypeSpecifier(PlainX.class, "(X)"); mockTypeSpecifier(PlainY.class, "(Y)"); mockOptional(Plain.class, "overriddenField", "true"); mockOptional(PlainX.class, "overriddenField", "false"); mockOptional(PlainY.class, "overriddenField", "false"); mockConstraint(Plain.class, "overriddenField", "Size[1]"); mockConstraint(PlainX.class, "overriddenField", "Size[x]"); mockConstraint(PlainY.class, "overriddenField", "Size[y]"); TypeMapping typeMapping = new TypeMapping(); typeMapping.mapSubtypes(Plain.class, PlainX.class, PlainY.class); FieldDocumentationGenerator generator = new FieldDocumentationGenerator(mapper.writer(), mapper.getDeserializationConfig(), javadocReader, constraintReader, typeMapping, SnippetTranslationManager.getDefaultResolver(), null); Type type = Plain.class; List<ExtendedFieldDescriptor> fieldDescriptions = cast(generator .generateDocumentation(type, mapper.getTypeFactory()).values()); assertThat(fieldDescriptions.size(), is(4)); assertThat(fieldDescriptions.get(0), is(descriptor("field", "String", "A field", "true"))); assertThat(fieldDescriptions.get(1), is(descriptor("overriddenField", "String", "An overridden field<br>Custom 1 (X)<br>Custom 2 (Y)", Arrays.asList("true", "false (X)", "false (Y)"), "Size[1]", "Size[x] (X)", "Size[y] (Y)"))); assertThat(fieldDescriptions.get(2), is(descriptor("x", "String", "A field X (X)", "true (X)"))); assertThat(fieldDescriptions.get(3), is(descriptor("y", "String", "A field Y (Y)", "true (Y)"))); }
@Test public void testGenerateDocumentationWithTags() throws Exception { ObjectMapper mapper = createMapper(); mockFieldComment(BasicTypes.class, "string", "A string"); mockFieldTag(BasicTypes.class, "string", "see", "this"); mockFieldComment(BasicTypes.class, "bool", "A boolean"); mockFieldTag(BasicTypes.class, "bool", "see", "<a href=\"xyz\">docs</a>"); FieldDocumentationGenerator generator = new FieldDocumentationGenerator(mapper.writer(), mapper.getDeserializationConfig(), javadocReader, constraintReader, typeMapping, SnippetTranslationManager.getDefaultResolver(), null); Type type = BasicTypes.class; List<ExtendedFieldDescriptor> result = cast(generator .generateDocumentation(type, mapper.getTypeFactory()).values()); assertThat(result.size(), is(4)); assertThat(result.get(0), is(descriptor("string", "String", "A string<br>See this.", "true"))); assertThat(result.get(1), is(descriptor("bool", "Boolean", "A boolean<br>See <a href=\"xyz\">docs</a>.", "true"))); }
@Test public void testGenerateDocumentationForRequiredProperties() throws Exception { ObjectMapper mapper = createMapper(); mockFieldComment(RequiredProperties.class, "string", "A string"); mockFieldComment(RequiredProperties.class, "number", "An integer"); FieldDocumentationGenerator generator = new FieldDocumentationGenerator(mapper.writer(), mapper.getDeserializationConfig(), javadocReader, constraintReader, typeMapping, SnippetTranslationManager.getDefaultResolver(), null); Type type = RequiredProperties.class; List<ExtendedFieldDescriptor> result = cast(generator .generateDocumentation(type, mapper.getTypeFactory()).values()); assertThat(result.size(), is(2)); assertThat(result.get(0), is(descriptor("string", "String", "A string", "false"))); assertThat(result.get(1), is(descriptor("number", "Integer", "An integer", "false"))); }