diff --git a/modules/swagger-core/src/main/java/io/swagger/jackson/ModelResolver.java b/modules/swagger-core/src/main/java/io/swagger/jackson/ModelResolver.java index 1a607ef6f1..6ee50d57d7 100644 --- a/modules/swagger-core/src/main/java/io/swagger/jackson/ModelResolver.java +++ b/modules/swagger-core/src/main/java/io/swagger/jackson/ModelResolver.java @@ -303,7 +303,7 @@ public Model resolve(JavaType type, ModelConverterContext context, Iterator beanClass = bean.getClassInfo().getAnnotated(); for (NamedType subtype : types) { - if (!beanClass.isAssignableFrom(subtype.getType())) { + final Class subtypeType = subtype.getType(); + if (!beanClass.isAssignableFrom(subtypeType)) { continue; } - final Model subtypeModel = context.resolve(subtype.getType()); + final Model subtypeModel = context.resolve(subtypeType); if (subtypeModel instanceof ModelImpl) { final ModelImpl impl = (ModelImpl) subtypeModel; + // check if model name was inherited + if (impl.getName().equals(model.getName())) { + impl.setName(_typeNameResolver.nameForType(_mapper.constructType(subtypeType), + TypeNameResolver.Options.SKIP_API_MODEL)); + } + // remove shared properties defined in the parent final Map baseProps = model.getProperties(); final Map subtypeProps = impl.getProperties(); diff --git a/modules/swagger-core/src/main/java/io/swagger/jackson/TypeNameResolver.java b/modules/swagger-core/src/main/java/io/swagger/jackson/TypeNameResolver.java index 3d9345e297..c1dfdd2f26 100644 --- a/modules/swagger-core/src/main/java/io/swagger/jackson/TypeNameResolver.java +++ b/modules/swagger-core/src/main/java/io/swagger/jackson/TypeNameResolver.java @@ -1,11 +1,17 @@ package io.swagger.jackson; -import com.fasterxml.jackson.databind.JavaType; import io.swagger.annotations.ApiModel; import io.swagger.util.PrimitiveType; + +import com.fasterxml.jackson.databind.JavaType; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.text.WordUtils; +import java.util.Arrays; +import java.util.Collections; +import java.util.EnumSet; +import java.util.Set; + /** * Helper class used for converting well-known (property) types into * Swagger type names. @@ -16,30 +22,39 @@ public class TypeNameResolver { protected TypeNameResolver() { } - public String nameForType(JavaType type) { + public String nameForType(JavaType type, Options... options) { + return nameForType(type, options.length == 0 ? Collections.emptySet() : + EnumSet.copyOf(Arrays.asList(options))); + } + + public String nameForType(JavaType type, Set options) { if (type.hasGenericTypes()) { - return nameForGenericType(type); + return nameForGenericType(type, options); } final String name = findStdName(type); - return (name == null) ? nameForClass(type) : name; + return (name == null) ? nameForClass(type, options) : name; } - protected String nameForClass(JavaType type) { - return nameForClass(type.getRawClass()); + protected String nameForClass(JavaType type, Set options) { + return nameForClass(type.getRawClass(), options); } - protected String nameForClass(Class cls) { + protected String nameForClass(Class cls, Set options) { + if (options.contains(Options.SKIP_API_MODEL)) { + return cls.getSimpleName(); + } final ApiModel model = cls.getAnnotation(ApiModel.class); final String modelName = model == null ? null : StringUtils.trimToNull(model.value()); return modelName == null ? cls.getSimpleName() : modelName; } - protected String nameForGenericType(JavaType type) { - final StringBuilder generic = new StringBuilder(nameForClass(type)); + protected String nameForGenericType(JavaType type, Set options) { + final StringBuilder generic = new StringBuilder(nameForClass(type, options)); final int count = type.containedTypeCount(); for (int i = 0; i < count; ++i) { final JavaType arg = type.containedType(i); - final String argName = PrimitiveType.fromType(arg) != null ? nameForClass(arg) : nameForType(arg); + final String argName = PrimitiveType.fromType(arg) != null ? nameForClass(arg, options) : + nameForType(arg, options); generic.append(WordUtils.capitalize(argName)); } return generic.toString(); @@ -48,4 +63,8 @@ protected String nameForGenericType(JavaType type) { protected String findStdName(JavaType type) { return PrimitiveType.getCommonName(type); } + + public enum Options { + SKIP_API_MODEL; + } } diff --git a/modules/swagger-core/src/test/java/io/swagger/ModelConverterTest.java b/modules/swagger-core/src/test/java/io/swagger/ModelConverterTest.java index 6e3301d22c..2ea9bd586e 100644 --- a/modules/swagger-core/src/test/java/io/swagger/ModelConverterTest.java +++ b/modules/swagger-core/src/test/java/io/swagger/ModelConverterTest.java @@ -27,6 +27,8 @@ import io.swagger.models.ModelWithNumbers; import io.swagger.models.ModelWithTuple2; import io.swagger.models.Person; +import io.swagger.models.composition.AbstractModelWithApiModel; +import io.swagger.models.composition.ModelWithUrlProperty; import io.swagger.models.composition.Pet; import io.swagger.models.properties.ArrayProperty; import io.swagger.models.properties.BaseIntegerProperty; @@ -100,6 +102,19 @@ public void honorApiModelName() { assertEquals(model, "MyModel"); } + @Test(description = "it should override an inherited model's name") + public void overrideInheritedModelName() { + final Map rootSchemas = readAll(AbstractModelWithApiModel.class); + assertEquals(rootSchemas.size(), 3); + assertTrue(rootSchemas.containsKey("MyProperty")); + assertTrue(rootSchemas.containsKey("ModelWithUrlProperty")); + assertTrue(rootSchemas.containsKey("ModelWithValueProperty")); + + final Map nestedSchemas = readAll(ModelWithUrlProperty.class); + assertEquals(nestedSchemas.size(), 1); + assertTrue(nestedSchemas.containsKey("MyProperty")); + } + @Test(description = "it should maintain property names") public void maintainPropertyNames() { final Map schemas = readAll(ModelPropertyName.class); diff --git a/modules/swagger-core/src/test/java/io/swagger/models/composition/AbstractModelWithApiModel.java b/modules/swagger-core/src/test/java/io/swagger/models/composition/AbstractModelWithApiModel.java new file mode 100644 index 0000000000..aa28ab5c4b --- /dev/null +++ b/modules/swagger-core/src/test/java/io/swagger/models/composition/AbstractModelWithApiModel.java @@ -0,0 +1,20 @@ +package io.swagger.models.composition; + +import io.swagger.annotations.ApiModel; + +import com.fasterxml.jackson.annotation.JsonSubTypes; + +@ApiModel("MyProperty") +@JsonSubTypes({@JsonSubTypes.Type(value = ModelWithUrlProperty.class), @JsonSubTypes.Type(value = ModelWithValueProperty.class)}) +public abstract class AbstractModelWithApiModel { + + private final String type; + + protected AbstractModelWithApiModel(String type) { + this.type = type; + } + + public String getType() { + return type; + } +} diff --git a/modules/swagger-core/src/test/java/io/swagger/models/composition/ModelWithUrlProperty.java b/modules/swagger-core/src/test/java/io/swagger/models/composition/ModelWithUrlProperty.java new file mode 100644 index 0000000000..20e75f348f --- /dev/null +++ b/modules/swagger-core/src/test/java/io/swagger/models/composition/ModelWithUrlProperty.java @@ -0,0 +1,22 @@ +package io.swagger.models.composition; + +import java.net.MalformedURLException; +import java.net.URL; + +public class ModelWithUrlProperty extends AbstractModelWithApiModel { + + private final URL url; + + public ModelWithUrlProperty(String type, String url) { + super(type); + try { + this.url = new URL(url); + } catch (MalformedURLException e) { + throw new IllegalArgumentException(e); + } + } + + public URL getUrl() { + return url; + } +} diff --git a/modules/swagger-core/src/test/java/io/swagger/models/composition/ModelWithValueProperty.java b/modules/swagger-core/src/test/java/io/swagger/models/composition/ModelWithValueProperty.java new file mode 100644 index 0000000000..10fa156299 --- /dev/null +++ b/modules/swagger-core/src/test/java/io/swagger/models/composition/ModelWithValueProperty.java @@ -0,0 +1,15 @@ +package io.swagger.models.composition; + +public class ModelWithValueProperty extends AbstractModelWithApiModel { + + private final String value; + + public ModelWithValueProperty(String type, String value) { + super(type); + this.value = value; + } + + public String getValue() { + return value; + } +}