package org.restdoc.server.impl.util; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.math.BigDecimal; import java.util.Collection; import java.util.Map; import org.restdoc.annotations.RestDocSchema; import org.restdoc.api.Schema; import org.restdoc.api.util.RestDocParser; import org.restdoc.server.impl.IRestDocGeneratorExtension; import org.restdoc.server.impl.RestDocException; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.module.jsonSchema.JsonSchema; import com.fasterxml.jackson.module.jsonSchema.JsonSchemaGenerator; /** * Copyright 2013 Cinovo AG<br> * <br> * * @author Thorsten Hoeger * */ public final class SchemaResolver { private static final ObjectMapper mapper = RestDocParser.createMapper(); private SchemaResolver() { // private utility class constructor } /** * same as getSchemaFromClassOrNull but throws {@link RestDocException} if no schema is found * * @param type the type to scan * @param schemaMap the map to add the schema to * @param ext the IRestDocGeneratorExtension to invoke on new schema * @return the schema URI */ public static String getSchemaFromType(final Type type, Map<String, Schema> schemaMap, IRestDocGeneratorExtension ext) { String schema = SchemaResolver.getSchemaFromTypeOrNull(type, schemaMap, ext); if (schema != null) { return schema; } final String s = String.format("SchemaType %s is not annotated with RestDocSchema.", type); throw new RestDocException(s); } /** * @param type the type to scan * @param schemaMap the map to add the schema to * @param ext the IRestDocGeneratorExtension to invoke on new schema * @return the schema URI */ public static String getSchemaFromTypeOrNull(final Type type, Map<String, Schema> schemaMap, IRestDocGeneratorExtension ext) { if (type.equals(Void.TYPE)) { return null; } if (type instanceof Class) { return SchemaResolver.getSchemaFromClassOrNull((Class<?>) type, schemaMap, ext); } // find generics if (type instanceof ParameterizedType) { ParameterizedType pt = (ParameterizedType) type; Type rawType = pt.getRawType(); // the surrounding type Type[] arguments = pt.getActualTypeArguments(); if (rawType instanceof Class) { Class<?> rawClass = (Class<?>) rawType; // something like Collection<T> if (Collection.class.isAssignableFrom(rawClass) && (arguments.length == 1)) { String nestedSchema = SchemaResolver.getSchemaFromTypeOrNull(arguments[0], schemaMap, ext); return nestedSchema + "[]"; } if (Map.class.isAssignableFrom(rawClass)) { return "object"; } } } return type.toString(); } private static String getSchemaFromClassOrNull(final Class<?> schemaClass, Map<String, Schema> schemaMap, IRestDocGeneratorExtension ext) { if (schemaClass.isArray()) { return SchemaResolver.getSchemaFromClassOrNull(schemaClass.getComponentType(), schemaMap, ext) + "[]"; } if (schemaClass.isAssignableFrom(String.class)) { return "string"; } if (schemaClass.isAssignableFrom(Integer.class) || schemaClass.equals(int.class)) { return "integer"; } if (schemaClass.isAssignableFrom(Long.class) || schemaClass.equals(long.class)) { return "long"; } if (schemaClass.isAssignableFrom(Boolean.class) || schemaClass.equals(boolean.class)) { return "boolean"; } if (schemaClass.isAssignableFrom(Double.class) || schemaClass.equals(double.class)) { return "double"; } if (schemaClass.isAssignableFrom(BigDecimal.class)) { return "double"; } if (schemaClass.isAnnotationPresent(RestDocSchema.class)) { final RestDocSchema docSchema = schemaClass.getAnnotation(RestDocSchema.class); final String schemaURI = docSchema.value(); if ((schemaMap != null) && !schemaMap.containsKey(schemaURI)) { try { JsonSchemaGenerator gen = new JsonSchemaGenerator(SchemaResolver.mapper); final JsonSchema schema = gen.generateSchema(schemaClass); final Schema s = new Schema(); s.setSchema(schema); schemaMap.put(schemaURI, s); if (ext != null) { ext.newSchema(schemaURI, s, schemaClass); } } catch (final JsonMappingException e) { throw new RestDocException("Error creating schema for URI: " + schemaURI, e); } } return schemaURI; } return schemaClass.getCanonicalName(); } }