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