springfox.documentation.spring.web.scanners.ApiModelReader.java Source code

Java tutorial

Introduction

Here is the source code for springfox.documentation.spring.web.scanners.ApiModelReader.java

Source

/*
 *
 *  Copyright 2015 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 springfox.documentation.spring.web.scanners;

import com.fasterxml.classmate.TypeResolver;
import com.google.common.base.Optional;
import com.google.common.collect.Maps;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import springfox.documentation.builders.ModelBuilder;
import springfox.documentation.schema.Model;
import springfox.documentation.schema.ModelProperty;
import springfox.documentation.schema.ModelProvider;
import springfox.documentation.spi.schema.contexts.ModelContext;
import springfox.documentation.spi.service.contexts.RequestMappingContext;
import springfox.documentation.spring.web.plugins.DocumentationPluginsManager;

import java.util.Map;
import java.util.Set;

import static com.google.common.collect.Maps.*;
import static com.google.common.collect.Sets.*;

@Component
public class ApiModelReader {
    private static final Logger LOG = LoggerFactory.getLogger(ApiModelReader.class);
    private final ModelProvider modelProvider;
    private final TypeResolver typeResolver;
    private final DocumentationPluginsManager pluginsManager;

    @Autowired
    public ApiModelReader(@Qualifier("cachedModels") ModelProvider modelProvider, TypeResolver typeResolver,
            DocumentationPluginsManager pluginsManager) {
        this.modelProvider = modelProvider;
        this.typeResolver = typeResolver;
        this.pluginsManager = pluginsManager;
    }

    public Map<String, Model> read(RequestMappingContext context) {

        Set<Class> ignorableTypes = newHashSet(context.getDocumentationContext().getIgnorableParameterTypes());
        Set<ModelContext> modelContexts = pluginsManager.modelContexts(context);
        Map<String, Model> modelMap = newHashMap(context.getModelMap());
        for (ModelContext each : modelContexts) {
            markIgnorablesAsHasSeen(typeResolver, ignorableTypes, each);
            Optional<Model> pModel = modelProvider.modelFor(each);
            if (pModel.isPresent()) {
                LOG.debug("Generated parameter model id: {}, name: {}, schema: {} models", pModel.get().getId(),
                        pModel.get().getName());
                mergeModelMap(modelMap, pModel.get());
            } else {
                LOG.debug("Did not find any parameter models for {}", each.getType());
            }
            populateDependencies(each, modelMap);
        }
        return modelMap;
    }

    @SuppressWarnings("unchecked")
    private void mergeModelMap(Map<String, Model> target, Model source) {
        String sourceModelKey = source.getId();

        if (!target.containsKey(sourceModelKey)) {
            //if we encounter completely unknown model, just add it
            LOG.debug("Adding a new model with key {}", sourceModelKey);
            target.put(sourceModelKey, source);
        } else {
            //we can encounter a known model with an unknown property
            //if (de)serialization is not symmetrical (@JsonIgnore on setter, @JsonProperty on getter).
            //In these cases, don't overwrite the entire model entry for that type, just add the unknown property.
            Model targetModelValue = target.get(sourceModelKey);

            Map<String, ModelProperty> targetProperties = targetModelValue.getProperties();
            Map<String, ModelProperty> sourceProperties = source.getProperties();

            Set<String> newSourcePropKeys = newHashSet(sourceProperties.keySet());
            newSourcePropKeys.removeAll(targetProperties.keySet());
            Map<String, ModelProperty> mergedTargetProperties = Maps.newHashMap(targetProperties);
            for (String newProperty : newSourcePropKeys) {
                LOG.debug("Adding a missing property {} to model {}", newProperty, sourceModelKey);
                mergedTargetProperties.put(newProperty, sourceProperties.get(newProperty));
            }

            Model mergedModel = new ModelBuilder().id(targetModelValue.getId()).name(targetModelValue.getName())
                    .type(targetModelValue.getType()).qualifiedType(targetModelValue.getQualifiedType())
                    .properties(mergedTargetProperties).description(targetModelValue.getDescription())
                    .baseModel(targetModelValue.getBaseModel()).discriminator(targetModelValue.getDiscriminator())
                    .subTypes(targetModelValue.getSubTypes()).build();

            target.put(sourceModelKey, mergedModel);
        }
    }

    private void markIgnorablesAsHasSeen(TypeResolver typeResolver, Set<Class> ignorableParameterTypes,
            ModelContext modelContext) {

        for (Class ignorableParameterType : ignorableParameterTypes) {
            modelContext.seen(typeResolver.resolve(ignorableParameterType));
        }
    }

    private void populateDependencies(ModelContext modelContext, Map<String, Model> modelMap) {
        Map<String, Model> dependencies = modelProvider.dependencies(modelContext);
        for (Model each : dependencies.values()) {
            mergeModelMap(modelMap, each);
        }
    }

}