1 package io.swagger.v3.core.converter;
2
3 import com.fasterxml.jackson.databind.type.TypeFactory;
4 import io.swagger.v3.core.jackson.ModelResolver;
5 import io.swagger.v3.core.util.Json;
6 import io.swagger.v3.oas.models.media.Schema;
7 import org.slf4j.Logger;
8 import org.slf4j.LoggerFactory;
9
10 import java.lang.reflect.Type;
11 import java.util.HashMap;
12 import java.util.HashSet;
13 import java.util.Iterator;
14 import java.util.List;
15 import java.util.Map;
16 import java.util.Map.Entry;
17 import java.util.ServiceLoader;
18 import java.util.Set;
19 import java.util.concurrent.CopyOnWriteArrayList;
20
21 public class ModelConverters {
22     private static final ModelConverters SINGLETON = new ModelConverters();
23     static Logger LOGGER = LoggerFactory.getLogger(ModelConverters.class);
24     private final List<ModelConverter> converters;
25     private final Set<String> skippedPackages = new HashSet<String>();
26     private final Set<String> skippedClasses = new HashSet<String>();
27
28     public ModelConverters() {
29         converters = new CopyOnWriteArrayList<>();
30         converters.add(new ModelResolver(Json.mapper()));
31     }
32
33     public static ModelConverters getInstance() {
34         return SINGLETON;
35     }
36
37     public void addConverter(ModelConverter converter) {
38         converters.add(0, converter);
39     }
40
41     public void removeConverter(ModelConverter converter) {
42         converters.remove(converter);
43     }
44
45     public void addPackageToSkip(String pkg) {
46         this.skippedPackages.add(pkg);
47     }
48
49     public void addClassToSkip(String cls) {
50         LOGGER.warn("skipping class " + cls);
51         this.skippedClasses.add(cls);
52     }
53
54     public Map<String, Schema> read(Type type) {
55         return read(new AnnotatedType().type(type));
56     }
57
58     public Map<String, Schema> read(AnnotatedType type) {
59         Map<String, Schema> modelMap = new HashMap<String, Schema>();
60         if (shouldProcess(type.getType())) {
61             ModelConverterContextImpl context = new ModelConverterContextImpl(
62                     converters);
63             Schema resolve = context.resolve(type);
64             for (Entry<String, Schema> entry : context.getDefinedModels()
65                     .entrySet()) {
66                 if (entry.getValue().equals(resolve)) {
67                     modelMap.put(entry.getKey(), entry.getValue());
68                 }
69             }
70         }
71         return modelMap;
72     }
73
74     public Map<String, Schema> readAll(Type type) {
75         return readAll(new AnnotatedType().type(type));
76     }
77
78     public Map<String, Schema> readAll(AnnotatedType type) {
79         if (shouldProcess(type.getType())) {
80             ModelConverterContextImpl context = new ModelConverterContextImpl(
81                     converters);
82
83             LOGGER.debug("ModelConverters readAll from " + type);
84             context.resolve(type);
85             return context.getDefinedModels();
86         }
87         return new HashMap<String, Schema>();
88     }
89
90     public ResolvedSchema readAllAsResolvedSchema(Type type) {
91         return readAllAsResolvedSchema(new AnnotatedType().type(type));
92     }
93     public ResolvedSchema readAllAsResolvedSchema(AnnotatedType type) {
94         if (shouldProcess(type.getType())) {
95             ModelConverterContextImpl context = new ModelConverterContextImpl(
96                     converters);
97
98             ResolvedSchema resolvedSchema = new ResolvedSchema();
99             resolvedSchema.schema = context.resolve(type);
100             resolvedSchema.referencedSchemas = context.getDefinedModels();
101
102             return resolvedSchema;
103         }
104         return null;
105     }
106
107     public ResolvedSchema resolveAsResolvedSchema(AnnotatedType type) {
108         ModelConverterContextImpl context = new ModelConverterContextImpl(
109                 converters);
110
111         ResolvedSchema resolvedSchema = new ResolvedSchema();
112         resolvedSchema.schema = context.resolve(type);
113         resolvedSchema.referencedSchemas = context.getDefinedModels();
114
115         return resolvedSchema;
116     }
117
118     public boolean isRegisteredAsSkippedClass(String className) {
119         return skippedClasses.contains(className);
120     }
121
122     private boolean shouldProcess(Type type) {
123         final Class<?> cls = TypeFactory.defaultInstance().constructType(type).getRawClass();
124         if (cls.isPrimitive()) {
125             return false;
126         }
127         String className = cls.getName();
128         for (String packageName : skippedPackages) {
129             if (className.startsWith(packageName)) {
130                 return false;
131             }
132         }
133         return !skippedClasses.contains(className);
134     }
135
136     static {
137         SINGLETON.skippedPackages.add("java.lang");
138
139         ServiceLoader<ModelConverter> loader = ServiceLoader.load(ModelConverter.class);
140         Iterator<ModelConverter> itr = loader.iterator();
141         while (itr.hasNext()) {
142             ModelConverter ext = itr.next();
143             if (ext == null) {
144                 LOGGER.error("failed to load extension " + ext);
145             } else {
146                 SINGLETON.addConverter(ext);
147                 LOGGER.debug("adding ModelConverter: " + ext);
148             }
149         }
150     }
151 }
152