com.wavemaker.tools.apidocs.tools.parser.impl.ReflectionModelParser.java Source code

Java tutorial

Introduction

Here is the source code for com.wavemaker.tools.apidocs.tools.parser.impl.ReflectionModelParser.java

Source

/**
 * Copyright  2013 - 2017 WaveMaker, Inc.
 *
 * 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 com.wavemaker.tools.apidocs.tools.parser.impl;

import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.validation.constraints.NotNull;

import org.apache.commons.lang3.StringUtils;
import org.reflections.ReflectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.Optional;
import com.wavemaker.tools.apidocs.tools.core.model.AbstractModel;
import com.wavemaker.tools.apidocs.tools.core.model.ComposedModel;
import com.wavemaker.tools.apidocs.tools.core.model.Model;
import com.wavemaker.tools.apidocs.tools.core.model.ModelImpl;
import com.wavemaker.tools.apidocs.tools.core.model.RefModel;
import com.wavemaker.tools.apidocs.tools.core.model.properties.Property;
import com.wavemaker.tools.apidocs.tools.parser.parser.ModelParser;
import com.wavemaker.tools.apidocs.tools.parser.parser.PropertyParser;
import com.wavemaker.tools.apidocs.tools.parser.util.ContextUtil;
import com.wavemaker.tools.apidocs.tools.parser.util.DataTypeUtil;
import com.wavemaker.tools.apidocs.tools.parser.util.MethodUtils;
import com.wavemaker.tools.apidocs.tools.parser.util.NonStaticMemberPredicate;
import com.wavemaker.tools.apidocs.tools.parser.util.Utils;
import com.wordnik.swagger.annotations.ApiModel;

/**
 * @author <a href="mailto:dilip.gundu@wavemaker.com">Dilip Kumar</a>
 * @since 10/11/14
 */
public class ReflectionModelParser implements ModelParser {

    private static final Logger LOGGER = LoggerFactory.getLogger(ReflectionModelParser.class);

    protected final Class<?> modelClass;

    public ReflectionModelParser(Class<?> modelClass) {
        this.modelClass = modelClass;
    }

    @Override
    public Model parse() {
        assertClass();
        Model model;
        List<Class<?>> superTypes = Utils.getAllFilteredSuperTypes(modelClass);
        if (!superTypes.isEmpty()) {
            model = parseComposedClass(modelClass, superTypes);
        } else {
            model = parseClass(modelClass);
        }
        ((AbstractModel) model).setFullyQualifiedName(DataTypeUtil.getFullyQualifiedName(modelClass));

        return model;
    }

    protected Model parseComposedClass(Class<?> classToScan, List<Class<?>> superTypes) {
        ComposedModel composedModel = new ComposedModel();

        // combining parent and interfaces
        List<Model> refModels = new LinkedList<>();
        for (final Class<?> superType : superTypes) {
            Optional<RefModel> modelOptional = ContextUtil.parseModel(superType);
            if (modelOptional.isPresent()) {
                refModels.add(modelOptional.get());
            }
        }
        composedModel.setAllOf(refModels);
        composedModel.child(parseClass(classToScan));
        return composedModel;
    }

    protected Model parseClass(Class<?> classToScan) {
        ModelImpl model = new ModelImpl();

        model.name(DataTypeUtil.getName(classToScan));
        if (classToScan.isAnnotationPresent(ApiModel.class)) {
            model.setDescription(classToScan.getAnnotation(ApiModel.class).description());
        }
        model.setProperties(getModelProperties(classToScan));
        model.setRequired(findRequiredFields(model.getProperties()));

        return model;
    }

    protected Map<String, Property> getModelProperties(Class<?> classToScan) {

        Map<String, Property> propertyMap;
        if (classToScan.isInterface()) {
            propertyMap = parsePropertiesUsingGetters(classToScan);
        } else {
            propertyMap = parsePropertiesUsingFields(classToScan);
        }

        return propertyMap;
    }

    protected Map<String, Property> parsePropertiesUsingGetters(Class<?> classToScan) {
        Map<String, Property> properties = new LinkedHashMap<>();

        Collection<Method> methods = MethodUtils.findGetterMethods(classToScan);

        for (final Method method : methods) {
            PropertyParser propertyParser = new PropertyParserImpl(method.getGenericReturnType());
            properties.put(MethodUtils.getPropertyName(method.getName()), propertyParser.parse());
        }

        return properties;
    }

    protected Map<String, Property> parsePropertiesUsingFields(Class<?> classToScan) {
        Map<String, Property> properties = new LinkedHashMap<>();
        Collection<Field> fields = Collections.EMPTY_LIST;
        try {
            fields = ReflectionUtils.getFields(classToScan, NonStaticMemberPredicate.getInstance());
        } catch (Throwable th) {
            // XXX should throw? if we throw error entire swagger generation will get failed.
            // Here issues with class loader, like NoClassDefFoundError and ClassNotFound exception.
            LOGGER.error("Error while reading fields for class:{}", classToScan, th);
        }
        for (Field field : fields) {
            PropertyParser parser = new PropertyParserImpl(field.getGenericType());
            final Property property = parser.parse();
            property.setRequired(isRequired(field));
            properties.put(findFieldName(field), property);
        }
        return properties;
    }

    protected String findFieldName(Field field) {
        String name = field.getName();
        if (field.isAnnotationPresent(JsonProperty.class)) {
            final String value = field.getAnnotation(JsonProperty.class).value();
            if (StringUtils.isNotBlank(value)) {
                name = value;
            }
        }
        return name;
    }

    protected boolean isRequired(AnnotatedElement field) {
        return field.isAnnotationPresent(NotNull.class);
    }

    protected List<String> findRequiredFields(Map<String, Property> propertyMap) {
        List<String> requiredFields = new ArrayList<>();

        if (propertyMap != null) {
            for (final Map.Entry<String, Property> entry : propertyMap.entrySet()) {
                if (entry.getValue().getRequired()) {
                    requiredFields.add(entry.getKey());
                }
            }
        }

        return requiredFields;
    }

    protected void assertClass() {
        if (modelClass == null) {
            throw new IllegalArgumentException("Invalid Model class");
        }
    }

}