Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;

/**
* The <code>PrimitiveType</code> enumeration defines a mapping of limited set
Expand Down Expand Up @@ -126,6 +128,12 @@ public DateTimeSchema createProperty() {
return new DateTimeSchema();
}
},
PARTIAL_TIME(java.time.LocalTime.class, "partial-time") {
@Override
public Schema createProperty() {
return new StringSchema().format("partial-time");
}
},
FILE(java.io.File.class, "file") {
@Override
public FileSchema createProperty() {
Expand All @@ -146,6 +154,25 @@ public Schema createProperty() {
* Joda lib.
*/
private static final Map<String, PrimitiveType> EXTERNAL_CLASSES;

/**
* Adds support for custom mapping of classes to primitive types
*/
private static Map<String, PrimitiveType> customClasses = new ConcurrentHashMap<String, PrimitiveType>();

/**
* class qualified names prefixes to be considered as "system" types
*/
private static Set<String> systemPrefixes = new ConcurrentHashMap<String, Boolean>().newKeySet();
/**
* class qualified names NOT to be considered as "system" types
*/
private static Set<String> nonSystemTypes = new ConcurrentHashMap<String, Boolean>().newKeySet();
/**
* package names NOT to be considered as "system" types
*/
private static Set<String> nonSystemTypePackages = new ConcurrentHashMap<String, Boolean>().newKeySet();

/**
* Alternative names for primitive types that have to be supported for
* backward compatibility.
Expand All @@ -157,6 +184,10 @@ public Schema createProperty() {
public static final Map<String, String> datatypeMappings;

static {
systemPrefixes.add("java.");
systemPrefixes.add("javax.");
nonSystemTypes.add("java.time.LocalTime");

final Map<String, String> dms = new HashMap<>();
dms.put("integer_int32", "integer");
dms.put("integer_", "integer");
Expand All @@ -173,6 +204,7 @@ public Schema createProperty() {
dms.put("string_uuid", "uuid");
dms.put("string_date", "date");
dms.put("string_date-time", "date-time");
dms.put("string_partial-time", "partial-time");
dms.put("string_password", "password");
dms.put("boolean", "boolean");
dms.put("object_", "object");
Expand Down Expand Up @@ -231,16 +263,63 @@ private PrimitiveType(Class<?> keyClass, String commonName) {
this.commonName = commonName;
}

/**
* Adds support for custom mapping of classes to primitive types
*
* @return Map of custom classes to primitive type
* @since 2.0.6
*/
public static Map<String, PrimitiveType> customClasses() {
return customClasses;
}

/**
* class qualified names prefixes to be considered as "system" types
*
* @return Mutable set of class qualified names prefixes to be considered as "system" types
* @since 2.0.6
*/
public static Set<String> systemPrefixes() {
return systemPrefixes;
}

/**
* class qualified names NOT to be considered as "system" types
*
* @return Mutable set of class qualified names NOT to be considered as "system" types
* @since 2.0.6
*/
public static Set<String> nonSystemTypes() {
return nonSystemTypes;
}

/**
* package names NOT to be considered as "system" types
*
* @return Mutable set of package names NOT to be considered as "system" types
* @since 2.0.6
*/
public static Set<String> nonSystemTypePackages() {
return nonSystemTypePackages;
}

public static PrimitiveType fromType(Type type) {
final Class<?> raw = TypeFactory.defaultInstance().constructType(type).getRawClass();
final PrimitiveType key = KEY_CLASSES.get(raw);
if (key != null) {
return key;
}

final PrimitiveType custom = customClasses.get(raw.getName());
if (custom != null) {
return custom;
}

final PrimitiveType external = EXTERNAL_CLASSES.get(raw.getName());
if (external != null) {
return external;
}

for (Map.Entry<Class<?>, PrimitiveType> entry : BASE_CLASSES.entrySet()) {
if (entry.getKey().isAssignableFrom(raw)) {
return entry.getValue();
Expand Down Expand Up @@ -302,4 +381,15 @@ private static class DateStub {
private DateStub() {
}
}

/**
* Convenience method to map LocalTime to string primitive with rfc3339 format partial-time.
* See https://xml2rfc.tools.ietf.org/public/rfc/html/rfc3339.html#anchor14
*
* @since 2.0.6
*/
public static void enablePartialTime() {
customClasses().put("org.joda.time.LocalTime", PrimitiveType.PARTIAL_TIME);
customClasses().put("java.time.LocalTime", PrimitiveType.PARTIAL_TIME);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -360,8 +360,13 @@ public static boolean isVoid(Type type) {
public static boolean isSystemType(JavaType type) {
// used while resolving container types to skip resolving system types; possibly extend by checking classloader
// and/or other packages
if (type.getRawClass().getName().startsWith("java")) {
return true;
for (String systemPrefix: PrimitiveType.systemPrefixes()) {
if (type.getRawClass().getName().startsWith(systemPrefix)) {
if ( !PrimitiveType.nonSystemTypes().contains(type.getRawClass().getName()) &&
!PrimitiveType.nonSystemTypePackages().contains(type.getRawClass().getPackage().getName())) {
return true;
}
}
}
if (type.isArrayType()) {
return true;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package io.swagger.v3.core.resolving;

import io.swagger.v3.core.converter.AnnotatedType;
import io.swagger.v3.core.converter.ModelConverterContextImpl;
import io.swagger.v3.core.jackson.ModelResolver;
import io.swagger.v3.core.matchers.SerializationMatchers;
import io.swagger.v3.core.resolving.resources.TestObject2992;
import io.swagger.v3.core.util.PrimitiveType;
import io.swagger.v3.oas.models.media.Schema;
import org.testng.annotations.Test;

public class Ticket2992Test extends SwaggerTestBase {

@Test
public void testLocalTime() throws Exception {

final ModelResolver modelResolver = new ModelResolver(mapper());

ModelConverterContextImpl context = new ModelConverterContextImpl(modelResolver);

Schema model = context
.resolve(new AnnotatedType(TestObject2992.class));

SerializationMatchers.assertEqualsToYaml(context.getDefinedModels(), "LocalTime:\n" +
" type: object\n" +
" properties:\n" +
" hour:\n" +
" type: integer\n" +
" format: int32\n" +
" minute:\n" +
" type: integer\n" +
" format: int32\n" +
" second:\n" +
" type: integer\n" +
" format: int32\n" +
" nano:\n" +
" type: integer\n" +
" format: int32\n" +
"TestObject2992:\n" +
" type: object\n" +
" properties:\n" +
" name:\n" +
" type: string\n" +
" a:\n" +
" $ref: '#/components/schemas/LocalTime'\n" +
" b:\n" +
" $ref: '#/components/schemas/LocalTime'\n" +
" c:\n" +
" $ref: '#/components/schemas/LocalTime'\n" +
" d:\n" +
" type: string\n" +
" format: date-time\n" +
" e:\n" +
" type: string\n" +
" format: date-time\n" +
" f:\n" +
" type: string\n" +
" format: date-time");

PrimitiveType.enablePartialTime();
context = new ModelConverterContextImpl(modelResolver);

context
.resolve(new AnnotatedType(TestObject2992.class));

SerializationMatchers.assertEqualsToYaml(context.getDefinedModels(), "TestObject2992:\n" +
" type: object\n" +
" properties:\n" +
" name:\n" +
" type: string\n" +
" a:\n" +
" type: string\n" +
" format: partial-time\n" +
" b:\n" +
" type: string\n" +
" format: partial-time\n" +
" c:\n" +
" type: string\n" +
" format: partial-time\n" +
" d:\n" +
" type: string\n" +
" format: date-time\n" +
" e:\n" +
" type: string\n" +
" format: date-time\n" +
" f:\n" +
" type: string\n" +
" format: date-time");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package io.swagger.v3.core.resolving.resources;

import java.time.LocalDateTime;
import java.time.LocalTime;

public class TestObject2992 {

private String name;
private LocalTime a;
private LocalTime b;
private LocalTime c;

private LocalDateTime d;
private LocalDateTime e;
private LocalDateTime f;


public LocalTime getA() {
return a;
}

public void setA(LocalTime a) {
this.a = a;
}

public LocalTime getB() {
return b;
}

public void setB(LocalTime b) {
this.b = b;
}

public LocalTime getC() {
return c;
}

public void setC(LocalTime c) {
this.c = c;
}

public LocalDateTime getD() {
return d;
}

public void setD(LocalDateTime d) {
this.d = d;
}

public LocalDateTime getE() {
return e;
}

public void setE(LocalDateTime e) {
this.e = e;
}

public LocalDateTime getF() {
return f;
}

public void setF(LocalDateTime f) {
this.f = f;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}