cn.msuno.swagger.spring.boot.autoconfigure.mappers.ModelMapper Maven / Gradle / Ivy
/*
*
* Copyright 2015-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*
*/
package cn.msuno.swagger.spring.boot.autoconfigure.mappers;
import static cn.msuno.swagger.spring.boot.autoconfigure.mappers.EnumMapper.maybeAddAllowableValues;
import static cn.msuno.swagger.spring.boot.autoconfigure.mappers.EnumMapper.safeBigDecimal;
import static cn.msuno.swagger.spring.boot.autoconfigure.mappers.Properties.defaultOrdering;
import static cn.msuno.swagger.spring.boot.autoconfigure.mappers.Properties.property;
import static cn.msuno.swagger.spring.boot.autoconfigure.mappers.Properties.voidProperties;
import static com.google.common.base.Predicates.not;
import static com.google.common.collect.Maps.filterEntries;
import static com.google.common.collect.Maps.newTreeMap;
import static springfox.documentation.schema.Maps.isMapType;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import org.mapstruct.Mapper;
import com.fasterxml.classmate.ResolvedType;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Multimap;
import io.swagger.models.ComposedModel;
import io.swagger.models.Model;
import io.swagger.models.ModelImpl;
import io.swagger.models.RefModel;
import io.swagger.models.Xml;
import io.swagger.models.properties.AbstractNumericProperty;
import io.swagger.models.properties.ArrayProperty;
import io.swagger.models.properties.MapProperty;
import io.swagger.models.properties.ObjectProperty;
import io.swagger.models.properties.Property;
import io.swagger.models.properties.StringProperty;
import springfox.documentation.schema.ModelProperty;
import springfox.documentation.schema.ModelReference;
import springfox.documentation.service.AllowableRangeValues;
import springfox.documentation.service.AllowableValues;
import springfox.documentation.service.ApiListing;
@Mapper
public abstract class ModelMapper {
public Map mapModels(Map from) {
if (from == null) {
return null;
}
Map map = newTreeMap();
InheritanceDeterminer determiner = new InheritanceDeterminer(from);
for (Map.Entry entry : from.entrySet()) {
String key = entry.getKey();
Model value;
if (determiner.hasParent(entry.getValue())) {
value = mapComposedModel(determiner.parent(entry.getValue()), entry.getValue());
} else {
value = mapModel(entry.getValue());
}
map.put(key, value);
}
return map;
}
private Model mapComposedModel(RefModel parent, springfox.documentation.schema.Model source) {
ComposedModel model = new ComposedModel()
.interfaces(Collections.singletonList(parent))
.child(mapModel(source));
model.setDescription(source.getDescription());
model.setExample(source.getExample());
model.setTitle(source.getName());
SortedMap sortedProperties = sort(source.getProperties());
Map modelProperties = mapProperties(sortedProperties);
model.setProperties(modelProperties);
return model;
}
private Model mapModel(springfox.documentation.schema.Model source) {
ModelImpl model = new ModelImpl()
.description(source.getDescription())
.discriminator(source.getDiscriminator())
.example(source.getExample())
.name(source.getName())
.xml(mapXml(source.getXml()));
SortedMap sortedProperties = sort(source.getProperties());
Map modelProperties = mapProperties(sortedProperties);
model.setProperties(modelProperties);
FluentIterable requiredFields = FluentIterable.from(source.getProperties().values())
.filter(requiredProperty())
.transform(propertyName());
model.setRequired(requiredFields.toList());
model.setSimple(false);
model.setType(ModelImpl.OBJECT);
model.setTitle(source.getName());
if (isMapType(source.getType())) {
Optional clazz = typeOfValue(source);
if (clazz.isPresent()) {
model.additionalProperties(property(clazz.get().getSimpleName()));
} else {
model.additionalProperties(new ObjectProperty());
}
}
return model;
}
private Map mapProperties(SortedMap properties) {
Map mappedProperties = new LinkedHashMap();
SortedMap nonVoidProperties = filterEntries(properties, not(voidProperties()));
for (Map.Entry propertyEntry : nonVoidProperties.entrySet()) {
mappedProperties.put(propertyEntry.getKey(), mapProperty(propertyEntry.getValue()));
}
return mappedProperties;
}
/**
* Returns a {@link TreeMap} where the keys are sorted by their respective property position values in ascending
* order.
*
* @param modelProperties properties to sort
* @return sorted properties by position and name
*/
private SortedMap sort(Map modelProperties) {
SortedMap sortedMap = new TreeMap(defaultOrdering(modelProperties));
sortedMap.putAll(modelProperties);
return sortedMap;
}
@VisibleForTesting
Optional typeOfValue(springfox.documentation.schema.Model source) {
Optional mapInterface = findMapInterface(source.getType());
if (mapInterface.isPresent()) {
if (mapInterface.get().getTypeParameters().size() == 2) {
return Optional.of((Class) mapInterface.get().getTypeParameters().get(1).getErasedType());
}
return Optional.of((Class) Object.class);
}
return Optional.absent();
}
private Optional findMapInterface(ResolvedType type) {
return Optional.fromNullable(type.findSupertype(Map.class));
}
private Property mapProperty(ModelProperty source) {
Property property = modelRefToProperty(source.getModelRef());
maybeAddAllowableValues(property, source.getAllowableValues());
if (property instanceof ArrayProperty) {
ArrayProperty arrayProperty = (ArrayProperty) property;
maybeAddAllowableValues(arrayProperty.getItems(), source.getAllowableValues());
}
if (property instanceof AbstractNumericProperty) {
AbstractNumericProperty numericProperty = (AbstractNumericProperty) property;
AllowableValues allowableValues = source.getAllowableValues();
if (allowableValues instanceof AllowableRangeValues) {
AllowableRangeValues range = (AllowableRangeValues) allowableValues;
numericProperty.maximum(safeBigDecimal(range.getMax()));
numericProperty.exclusiveMaximum(range.getExclusiveMax());
numericProperty.minimum(safeBigDecimal(range.getMin()));
numericProperty.exclusiveMinimum(range.getExclusiveMin());
}
}
if (property instanceof StringProperty) {
StringProperty stringProperty = (StringProperty) property;
AllowableValues allowableValues = source.getAllowableValues();
if (allowableValues instanceof AllowableRangeValues) {
AllowableRangeValues range = (AllowableRangeValues) allowableValues;
stringProperty.maxLength(safeInteger(range.getMax()));
stringProperty.minLength(safeInteger(range.getMin()));
}
if (source.getPattern() != null) {
stringProperty.setPattern(source.getPattern());
}
stringProperty.setDefault(source.getDefaultValue());
}
Map extensions = new VendorExtensionsMapper().mapExtensions(source.getVendorExtensions());
if (property != null) {
property.setDescription(source.getDescription());
property.setName(source.getName());
property.setRequired(source.isRequired());
property.setReadOnly(source.isReadOnly());
property.setAllowEmptyValue(source.isAllowEmptyValue());
property.setExample(source.getExample());
property.getVendorExtensions().putAll(extensions);
property.setXml(mapXml(source.getXml()));
}
return property;
}
private Xml mapXml(springfox.documentation.schema.Xml xml) {
if (xml == null) {
return null;
}
return new Xml()
.name(xml.getName())
.attribute(xml.getAttribute())
.namespace(xml.getNamespace())
.prefix(xml.getPrefix())
.wrapped(xml.getWrapped());
}
static Integer safeInteger(String doubleString) {
try {
return Integer.valueOf(doubleString);
} catch (NumberFormatException e) {
return null;
}
}
static Property modelRefToProperty(ModelReference modelRef) {
if (modelRef == null || "void".equalsIgnoreCase(modelRef.getType())) {
return null;
}
Property responseProperty;
if (modelRef.isCollection()) {
responseProperty = property(modelRef);
} else if (modelRef.isMap()) {
responseProperty = new MapProperty(property(modelRef.itemModel().get()));
} else {
responseProperty = property(modelRef.getType());
}
maybeAddAllowableValues(responseProperty, modelRef.getAllowableValues());
return responseProperty;
}
Map modelsFromApiListings(Multimap apiListings) {
Map definitions = newTreeMap();
for (ApiListing each : apiListings.values()) {
definitions.putAll(each.getModels());
}
return mapModels(definitions);
}
private Function propertyName() {
return new Function() {
@Override
public String apply(ModelProperty input) {
return input.getName();
}
};
}
private Predicate requiredProperty() {
return new Predicate() {
@Override
public boolean apply(ModelProperty input) {
return input.isRequired();
}
};
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy