com.github.davidmoten.oas3.internal.Common Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of openapi-to-plantuml Show documentation
Show all versions of openapi-to-plantuml Show documentation
Generates PlantUML file from an OpenAPI 3.0 Definition
package com.github.davidmoten.oas3.internal;
import static java.util.Collections.emptyList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import com.github.davidmoten.guavamini.Preconditions;
import com.github.davidmoten.oas3.internal.model.Association;
import com.github.davidmoten.oas3.internal.model.AssociationType;
import com.github.davidmoten.oas3.internal.model.Class;
import com.github.davidmoten.oas3.internal.model.ClassType;
import com.github.davidmoten.oas3.internal.model.Field;
import com.github.davidmoten.oas3.internal.model.Inheritance;
import com.github.davidmoten.oas3.internal.model.Model;
import com.github.davidmoten.oas3.internal.model.Relationship;
import com.github.davidmoten.oas3.internal.model.SimpleType;
import io.swagger.v3.oas.models.media.ArraySchema;
import io.swagger.v3.oas.models.media.BinarySchema;
import io.swagger.v3.oas.models.media.BooleanSchema;
import io.swagger.v3.oas.models.media.ByteArraySchema;
import io.swagger.v3.oas.models.media.ComposedSchema;
import io.swagger.v3.oas.models.media.DateSchema;
import io.swagger.v3.oas.models.media.DateTimeSchema;
import io.swagger.v3.oas.models.media.EmailSchema;
import io.swagger.v3.oas.models.media.FileSchema;
import io.swagger.v3.oas.models.media.IntegerSchema;
import io.swagger.v3.oas.models.media.MapSchema;
import io.swagger.v3.oas.models.media.NumberSchema;
import io.swagger.v3.oas.models.media.ObjectSchema;
import io.swagger.v3.oas.models.media.PasswordSchema;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.media.StringSchema;
import io.swagger.v3.oas.models.media.UUIDSchema;
final class Common {
private Common() {
// prevent instantiation
}
private static final Set SIMPLE_TYPES_WITHOUT_BRACKETS = Arrays
.stream(SimpleType.values()) //
.map(x -> x.toString().replace("[]", "")) //
.collect(Collectors.toSet());
static Model toModelClass(String name, Schema> schema, Names names, ClassType classType) {
List fields = new ArrayList<>();
boolean isEnum = false;
List relationships = new ArrayList<>();
List classes = new ArrayList<>();
if (schema.get$ref() != null) {
// this is an alias case for a schema
String otherClassName = names.refToClassName(schema.get$ref()).className();
relationships.add(Association.from(name).to(otherClassName).one().build());
} else if (schema instanceof ComposedSchema) {
ComposedSchema s = (ComposedSchema) schema;
if (s.getOneOf() != null) {
addInheritance(classes, relationships, name, s.getOneOf(), names);
} else if (s.getAnyOf() != null) {
addInheritance(classes, relationships, name, s.getAnyOf(), names);
} else if (s.getAllOf() != null) {
addMixedTypeAll(classes, relationships, name, s.getAllOf(), null, names);
} else {
throw new RuntimeException("unexpected");
}
} else if (schema.getProperties() != null) {
final Set required;
if (schema.getRequired() != null) {
required = new HashSet<>(schema.getRequired());
} else {
required = Collections.emptySet();
}
schema.getProperties().entrySet().forEach(entry -> {
String property = entry.getKey();
Schema> sch = entry.getValue();
if (sch instanceof ComposedSchema) {
ComposedSchema s = (ComposedSchema) sch;
@SuppressWarnings("rawtypes")
final List list;
final AssociationType associationType;
boolean req = required.contains(property);
boolean isAll = false;
if (s.getOneOf() != null) {
list = s.getOneOf();
associationType = req ? AssociationType.ONE : AssociationType.ZERO_ONE;
} else if (s.getAnyOf() != null) {
list = s.getAnyOf();
associationType = req ? AssociationType.ONE : AssociationType.ZERO_ONE;
} else if (s.getAllOf() != null) {
list = s.getAllOf();
isAll = true;
associationType = null;
} else {
// don't expect this path but be defensive
list = emptyList();
associationType = null;
}
if (!list.isEmpty()) {
if (isAll) {
addMixedTypeAll(classes, relationships, name, list, property, names);
} else {
addInheritanceForProperty(classes, relationships, name, list, property,
associationType, names);
}
}
} else if (sch.get$ref() != null) {
String ref = sch.get$ref();
String otherClassName = names.refToClassName(ref).className();
addToOne(relationships, name, otherClassName, property,
required.contains(entry.getKey()), false);
} else {
Optional t = getUmlTypeName(sch, names);
if (t.isPresent()) {
String type = t.get();
if (isComplexArrayType(type)) {
addArray(name, classes, relationships, property, (ArraySchema) sch,
names);
} else if (type.equals("object")) {
// create anon class
String otherClassName = names.nextClassName(name + "." + property);
Model m = toModelClass(otherClassName, sch, names, classType);
classes.addAll(m.classes());
relationships.addAll(m.relationships());
addToOne(relationships, name, otherClassName, property,
required.contains(property), true);
} else if (type.equals("map")) {
MapSchema ms = (MapSchema) sch;
Schema> valueSchema = ((Schema>) ms.getAdditionalProperties());
if (valueSchema.get$ref() != null) {
String keyClassName = names.nextClassName(name + "." + property);
ObjectSchema keySchema = new ObjectSchema();
keySchema.addProperty("key", new StringSchema());
keySchema.addRequiredItem("key");
Model m = toModelClass(keyClassName, keySchema, names, ClassType.SCHEMA);
classes.addAll(m.classes());
relationships.addAll(m.relationships());
addToMany(relationships, name, keyClassName, property, true);
String valueClassName = names.refToClassName(valueSchema.get$ref()).className();
addToOne(relationships, keyClassName, valueClassName, "value", true, false);
} else {
fields.add(new Field(entry.getKey(), "string -> string", type.endsWith("]"),
true));
}
} else {
fields.add(new Field(entry.getKey(), type, type.endsWith("]"),
required.contains(entry.getKey())));
}
}
}
});
} else if (schema instanceof ArraySchema) {
ArraySchema a = (ArraySchema) schema;
Schema> items = a.getItems();
String ref = items.get$ref();
String otherClassName;
if (ref != null) {
otherClassName = names.refToClassName(ref).className();
} else {
// create anon class
otherClassName = names.nextClassName(name);
Model m = toModelClass(otherClassName, items, names, classType);
classes.addAll(m.classes());
relationships.addAll(m.relationships());
}
addToMany(relationships, name, otherClassName);
} else if (!(schema instanceof ObjectSchema)) {
// has no properties so ignore ObjectSchema
Optional t = getUmlTypeName(schema, names);
if (t.isPresent()) {
String type = t.get();
if (("string".equals(type) || "integer".equals(type) || "decimal".equals(type)) //
&& !isEmpty(schema.getEnum())) {
isEnum = true;
for (Object item: schema.getEnum()) {
fields.add(new Field(item.toString(), type, false, false));
}
} else {
fields.add(new Field("value", type, type.endsWith("]"), true));
}
}
}
classes.add(new Class(name, classType, fields, isEnum));
return new Model(classes, relationships);
}
private static boolean isEmpty(List> list) {
return list == null || list.isEmpty();
}
private static boolean isComplexArrayType(String type) {
return type.endsWith("[]") && !isSimpleType(type);
}
static boolean isSimpleType(String s) {
return SIMPLE_TYPES_WITHOUT_BRACKETS.contains(s.replace("[", "").replace("]", ""));
}
private static void addArray(String name, List classes, List relationships,
String property, ArraySchema a, Names names) {
Preconditions.checkNotNull(property);
// is array of items
Schema> items = a.getItems();
String ref = items.get$ref();
final String otherClassName;
if (ref != null) {
otherClassName = names.refToClassName(ref).className();
} else {
// create anon class
otherClassName = names.nextClassName(name + "." + property);
Model m = toModelClass(otherClassName, items, names, ClassType.SCHEMA);
classes.addAll(m.classes());
relationships.addAll(m.relationships());
}
addToMany(relationships, name, otherClassName, property);
}
private static void addMixedTypeAll(List classes, List relationships,
String name, @SuppressWarnings("rawtypes") List schemas, String propertyName,
Names names) {
List otherClassNames = addAnonymousClassesAndReturnOtherClassNames(classes,
relationships, name, schemas, names, propertyName);
for (String otherClassName : otherClassNames) {
addToOne(relationships, name, otherClassName, propertyName, true, false);
}
}
private static void addInheritanceForProperty(List classes,
List relationships, String name,
@SuppressWarnings("rawtypes") List schemas, String propertyName,
AssociationType associationType, Names names) {
List otherClassNames = addAnonymousClassesAndReturnOtherClassNames(classes,
relationships, name, schemas, names, propertyName);
Inheritance inheritance = new Inheritance(name, otherClassNames, associationType,
Optional.of(propertyName));
relationships.add(inheritance);
}
private static void addInheritance(List classes, List relationships,
String name, @SuppressWarnings("rawtypes") List schemas, Names names) {
List otherClassNames = addAnonymousClassesAndReturnOtherClassNames(classes,
relationships, name, schemas, names, null);
relationships
.add(new Inheritance(name, otherClassNames, AssociationType.ONE, Optional.empty()));
}
private static List addAnonymousClassesAndReturnOtherClassNames(List classes,
List relationships, String name,
@SuppressWarnings("rawtypes") List schemas, Names names, String property) {
List otherClassNames = schemas.stream() //
.map(s -> {
if (s.get$ref() != null) {
return names.refToClassName(s.get$ref()).className();
} else {
String className = names
.nextClassName(name + (property == null ? "" : "." + property));
Model m = toModelClass(className, s, names, ClassType.SCHEMA);
classes.addAll(m.classes());
relationships.addAll(m.relationships());
return className;
}
}).collect(Collectors.toList());
return otherClassNames;
}
private static void addToMany(List relationships, String name,
String otherClassName) {
addToMany(relationships, name, otherClassName, null);
}
private static void addToMany(List relationships, String name,
String otherClassName, String property) {
addToMany(relationships, name, otherClassName, property, false);
}
private static void addToMany(List relationships, String name, String otherClassName, String property,
boolean owns) {
relationships.add(Association.from(name).to(otherClassName).many()
.propertyOrParameterName(Optional.ofNullable(property)).owns(owns).build());
}
private static void addToOne(List relationships, String name,
String otherClassName, String property, boolean isToOne, boolean owns) {
relationships.add(Association //
.from(name) //
.to(otherClassName) //
.type(isToOne ? AssociationType.ONE : AssociationType.ZERO_ONE) //
.propertyOrParameterName(//
property == null || property.equals(otherClassName) ? Optional.empty()
: Optional.of(property))
.owns(owns) //
.build());
}
private static Optional getUmlTypeName(Schema> schema, Names names) {
return getUmlTypeName(schema.get$ref(), schema, names);
}
static Optional getUmlTypeName(String ref, Schema> schema, Names names) {
final String type;
if (ref != null) {
type = names.refToClassName(ref).className();
} else if (schema == null) {
type = null;
} else if (schema instanceof StringSchema) {
type = "string";
} else if (schema instanceof BooleanSchema) {
type = "boolean";
} else if (schema instanceof DateTimeSchema) {
type = "timestamp";
} else if (schema instanceof DateSchema) {
type = "date";
} else if (schema instanceof NumberSchema) {
type = "decimal";
} else if (schema instanceof IntegerSchema) {
type = "integer";
} else if (schema instanceof ArraySchema) {
ArraySchema a = (ArraySchema) schema;
type = getUmlTypeName(a.getItems(), names) //
.orElseThrow(() -> new RuntimeException("unexpected")) + "[]";
} else if (schema instanceof BinarySchema) {
type = "byte[]";
} else if (schema instanceof ByteArraySchema) {
type = "byte[]";
} else if (schema instanceof ObjectSchema) {
type = "object";
} else if (schema instanceof FileSchema) {
type = "string";
} else if (schema instanceof PasswordSchema) {
type = "string";
} else if (schema instanceof EmailSchema) {
type = "string";
} else if (schema instanceof UUIDSchema) {
type = "string";
} else if (schema instanceof MapSchema) {
type = "map";
} else if (schema instanceof ComposedSchema) {
// TODO handle ComposedSchema
type = "string";
} else if ("string".equals(schema.getType())) {
type = "string";
} else if (schema.get$ref() != null) {
type = names.refToClassName(schema.get$ref()).className();
} else if (schema.getType() == null) {
type = null;
} else {
throw new RuntimeException("not expected" + schema);
}
return Optional.ofNullable(type);
}
}