Skip to content

Commit

Permalink
Merge 4abc616 into d9fb245
Browse files Browse the repository at this point in the history
  • Loading branch information
skynetsonar committed Nov 18, 2020
2 parents d9fb245 + 4abc616 commit caaf3fe
Show file tree
Hide file tree
Showing 3 changed files with 254 additions and 160 deletions.
Expand Up @@ -15,6 +15,17 @@
*/
package com.mercateo.common.rest.schemagen;

import com.fasterxml.jackson.annotation.JsonUnwrapped;
import com.google.common.collect.ImmutableMap;
import com.mercateo.common.rest.schemagen.JsonProperty.Builder;
import com.mercateo.common.rest.schemagen.generator.ImmutableJsonPropertyResult;
import com.mercateo.common.rest.schemagen.generator.JsonPropertyResult;
import com.mercateo.common.rest.schemagen.generator.ObjectContext;
import com.mercateo.common.rest.schemagen.generator.ObjectContextBuilder;
import com.mercateo.common.rest.schemagen.generator.PathContext;
import com.mercateo.common.rest.schemagen.generator.ReferencedJsonPropertyFinder;
import com.mercateo.common.rest.schemagen.generictype.GenericType;
import com.mercateo.common.rest.schemagen.util.EnumUtil;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
Expand All @@ -27,30 +38,24 @@
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import com.fasterxml.jackson.annotation.JsonUnwrapped;
import com.google.common.collect.ImmutableMap;
import com.mercateo.common.rest.schemagen.generator.ImmutableJsonPropertyResult;
import com.mercateo.common.rest.schemagen.generator.JsonPropertyResult;
import com.mercateo.common.rest.schemagen.generator.ObjectContext;
import com.mercateo.common.rest.schemagen.generator.ObjectContextBuilder;
import com.mercateo.common.rest.schemagen.generator.PathContext;
import com.mercateo.common.rest.schemagen.generator.ReferencedJsonPropertyFinder;
import com.mercateo.common.rest.schemagen.generictype.GenericType;
import com.mercateo.common.rest.schemagen.util.EnumUtil;

public class SchemaPropertyGenerator {

private static final Map<Class<?>, JsonProperty> builtins = ImmutableMap.of( //
OffsetDateTime.class, JsonProperty.builderFor(String.class).withName("n/a").withFormat("date-time").build(), //
// TODO: "full-time" is no standard JSON-schema format
OffsetTime.class, JsonProperty.builderFor(String.class).withName("n/a").withFormat("full-time").build(), //
// TODO: "uuid" is no standard JSON-schema format, the pattern is
// for a v4 UUID. Is that enough?
UUID.class, JsonProperty.builderFor(String.class).withName("n/a").withFormat("uuid")
.withPattern("^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$").build());
private static final Map<Class<?>, Function<Object, String>> builtinsValueSerializers = ImmutableMap.of(
UUID.class, Object::toString);

private static final Map<Class<?>, JsonProperty> builtins = ImmutableMap.of( //
OffsetDateTime.class, JsonProperty.builderFor(String.class).withName("n/a").withFormat("date-time").build(),
//
// TODO: "full-time" is no standard JSON-schema format
OffsetTime.class, JsonProperty.builderFor(String.class).withName("n/a").withFormat("full-time").build(), //
// TODO: "uuid" is no standard JSON-schema format, the pattern is
// for a v4 UUID. Is that enough?
UUID.class, JsonProperty.builderFor(String.class).withName("n/a").withFormat("uuid")
.withPattern("^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$").build());

private final ReferencedJsonPropertyFinder referencedJsonPropertyFinder;

Expand All @@ -70,7 +75,7 @@ public SchemaPropertyGenerator() {
* @return property hierarchy
*/
public JsonPropertyResult generateSchemaProperty(ObjectContextBuilder<?> objectContextBuilder,
SchemaPropertyContext context) {
SchemaPropertyContext context) {
return generateSchemaProperty(objectContextBuilder.build(), context);
}

Expand All @@ -84,8 +89,8 @@ public JsonPropertyResult generateSchemaProperty(ObjectContextBuilder<?> objectC
* @return property hierarchy
*/
public JsonPropertyResult generateSchemaProperty(ObjectContext<?> objectContext,
SchemaPropertyContext context) {
JsonProperty rootJsonProperty = determineProperty("#", objectContext, new PathContext(), context);
SchemaPropertyContext context) {
JsonProperty rootJsonProperty = determineProperty("#", objectContext, new PathContext(), context);
rootJsonProperty = updateName(rootJsonProperty, objectContext.getRawType().getSimpleName());
return ImmutableJsonPropertyResult.of(
rootJsonProperty,
Expand All @@ -97,7 +102,7 @@ private JsonProperty updateName(JsonProperty jsonProperty, String name) {
}

private List<JsonProperty> getProperties(final ObjectContext<?> objectContext,
final PathContext pathContext, final SchemaPropertyContext context) {
final PathContext pathContext, final SchemaPropertyContext context) {

final Map<Field, ObjectContext> fieldContextMap = getFieldContextMap(objectContext, context);

Expand All @@ -121,13 +126,13 @@ private List<JsonProperty> getProperties(final ObjectContext<?> objectContext,
}

private Map<Field, ObjectContext> getFieldContextMap(ObjectContext objectContext,
SchemaPropertyContext context) {
SchemaPropertyContext context) {
return getFieldContextMap(objectContext, new HashMap<>(), new HashSet<>(), context);
}

private Map<Field, ObjectContext> getFieldContextMap(ObjectContext objectContext,
final Map<Field, ObjectContext> fieldContextMap, final Set<Type> unwrappedTypes,
SchemaPropertyContext context) {
final Map<Field, ObjectContext> fieldContextMap, final Set<Type> unwrappedTypes,
SchemaPropertyContext context) {
do {
for (Field field : objectContext.getGenericType().getDeclaredFields()) {
addFieldToMap(field, objectContext, fieldContextMap, unwrappedTypes, context);
Expand All @@ -137,8 +142,8 @@ private Map<Field, ObjectContext> getFieldContextMap(ObjectContext objectContext
}

private void addFieldToMap(Field field, ObjectContext objectContext,
Map<Field, ObjectContext> fieldContextMap, Set<Type> unwrappedTypes,
SchemaPropertyContext context) {
Map<Field, ObjectContext> fieldContextMap, Set<Type> unwrappedTypes,
SchemaPropertyContext context) {
if (objectContext.isApplicable(field, context)) {
if (field.getAnnotation(JsonUnwrapped.class) != null) {
fieldContextMap.putAll(getUnwrappedFieldsMap(field, objectContext, unwrappedTypes,
Expand All @@ -150,7 +155,7 @@ private void addFieldToMap(Field field, ObjectContext objectContext,
}

private Map<Field, ObjectContext> getUnwrappedFieldsMap(Field field,
ObjectContext objectContext, Set<Type> unwrappedTypes, SchemaPropertyContext context) {
ObjectContext objectContext, Set<Type> unwrappedTypes, SchemaPropertyContext context) {
objectContext = objectContext.forField(field);
final Type unwrappedType = objectContext.getType();
if (unwrappedTypes.contains(unwrappedType)) {
Expand All @@ -170,21 +175,41 @@ private int byName(Field field1, Field field2) {
return field1.getName().compareTo(field2.getName());
}

private JsonProperty determineProperty(String name, ObjectContext<?> objectContext, PathContext pathContext,
SchemaPropertyContext context) {
JsonProperty determineObjectProperty = determineObjectProperty(name, objectContext, pathContext, context);
private JsonProperty determineProperty(String name, ObjectContext<?> objectContext, PathContext pathContext,
SchemaPropertyContext context) {
JsonProperty determineObjectProperty = determineObjectProperty(name, objectContext, pathContext, context);

if (builtins.containsKey(objectContext.getRawType())) {
JsonProperty jsonPropertyTemplate = builtins.get(objectContext.getRawType());
return JsonProperty.builderFrom(jsonPropertyTemplate).withName(name)
.withIsRequired(determineObjectProperty.isRequired()).build();
} else {
return determineObjectProperty;
}
}
if (builtins.containsKey(objectContext.getRawType())) {
return getBuiltinsJsonProperty(name, objectContext, determineObjectProperty);
} else {
return determineObjectProperty;
}
}

private JsonProperty getBuiltinsJsonProperty(String name, ObjectContext<?> objectContext,
JsonProperty determineObjectProperty) {
JsonProperty jsonPropertyTemplate = builtins.get(objectContext.getRawType());

final Builder builder = JsonProperty.builderFrom(jsonPropertyTemplate).withName(name)
.withIsRequired(determineObjectProperty.isRequired())
.withPath(determineObjectProperty.getPath());
if (builtinsValueSerializers.containsKey(objectContext.getRawType())) {
final Function<Object, String> serialize = builtinsValueSerializers
.get(objectContext.getRawType());
if (determineObjectProperty.getAllowedValues() != null) {
builder.withAllowedValues(determineObjectProperty.getAllowedValues().stream().map(serialize)
.collect(Collectors.toList()));
}
if (determineObjectProperty.getDefaultValue() != null) {
final Object defaultValue = determineObjectProperty.getDefaultValue();
builder.withDefaultValue(serialize.apply(defaultValue));
}
}
return builder.build();
}

private JsonProperty determineObjectProperty(String name, ObjectContext<?> objectContext,
PathContext pathContext, SchemaPropertyContext context) {
PathContext pathContext, SchemaPropertyContext context) {

JsonProperty.Builder builder = JsonProperty.builderFor(objectContext).withName(name);

Expand All @@ -203,31 +228,32 @@ private JsonProperty determineObjectProperty(String name, ObjectContext<?> objec
}

private List<JsonProperty> getNestedProperties(ObjectContext<?> objectContext,
PathContext pathContext, SchemaPropertyContext context) {
PathContext pathContext, SchemaPropertyContext context) {
List<JsonProperty> properties = new ArrayList<>();
switch (objectContext.getPropertyType()) {
case OBJECT:
final PropertySubType subType = objectContext.getPropertySubType();
switch (subType) {
case DICT:
properties = getDictProperties(objectContext, pathContext, context);
break;
default:
properties = getProperties(objectContext, pathContext, context);
}
case OBJECT:
final PropertySubType subType = objectContext.getPropertySubType();
switch (subType) {
case DICT:
properties = getDictProperties(objectContext, pathContext, context);
break;
default:
properties = getProperties(objectContext, pathContext, context);
}
break;

case ARRAY:
properties.add(determineProperty("", objectContext.getContained(), pathContext, context));
break;
case ARRAY:
properties.add(determineProperty("", objectContext.getContained(), pathContext, context));
break;

default:
break;
default:
break;
}
return properties;
}

private List<JsonProperty> getDictProperties(ObjectContext<?> objectContext, PathContext pathContext, SchemaPropertyContext context) {
private List<JsonProperty> getDictProperties(ObjectContext<?> objectContext, PathContext pathContext,
SchemaPropertyContext context) {
ParameterizedType parameterizedType = (ParameterizedType) objectContext.getType();

final Class<?> keyType = (Class<?>) parameterizedType.getActualTypeArguments()[0];
Expand Down

0 comments on commit caaf3fe

Please sign in to comment.