org.assertj.assertions.generator.description.converter.ClassToClassDescriptionConverter.java Source code

Java tutorial

Introduction

Here is the source code for org.assertj.assertions.generator.description.converter.ClassToClassDescriptionConverter.java

Source

/**
 * 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.
 *
 * Copyright 2012-2015 the original author or authors.
 */
package org.assertj.assertions.generator.description.converter;

import static org.apache.commons.lang3.StringUtils.remove;
import static org.assertj.assertions.generator.util.ClassUtil.declaredGetterMethodsOf;
import static org.assertj.assertions.generator.util.ClassUtil.declaredPublicFieldsOf;
import static org.assertj.assertions.generator.util.ClassUtil.getterMethodsOf;
import static org.assertj.assertions.generator.util.ClassUtil.inheritsCollectionOrIsIterable;
import static org.assertj.assertions.generator.util.ClassUtil.isArray;
import static org.assertj.assertions.generator.util.ClassUtil.nonStaticPublicFieldsOf;
import static org.assertj.assertions.generator.util.ClassUtil.propertyNameOf;

import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import org.assertj.assertions.generator.description.ClassDescription;
import org.assertj.assertions.generator.description.FieldDescription;
import org.assertj.assertions.generator.description.GetterDescription;
import org.assertj.assertions.generator.description.TypeDescription;
import org.assertj.assertions.generator.description.TypeName;
import org.assertj.assertions.generator.util.ClassUtil;

import com.google.common.annotations.VisibleForTesting;

public class ClassToClassDescriptionConverter implements ClassDescriptionConverter<Class<?>> {

    public ClassDescription convertToClassDescription(Class<?> clazz) {
        ClassDescription classDescription = new ClassDescription(new TypeName(clazz));
        classDescription.addGetterDescriptions(getterDescriptionsOf(clazz));
        classDescription.addFieldDescriptions(fieldDescriptionsOf(clazz));
        classDescription.addDeclaredGetterDescriptions(declaredGetterDescriptionsOf(clazz));
        classDescription.addDeclaredFieldDescriptions(declaredFieldDescriptionsOf(clazz));
        classDescription.setSuperType(clazz.getSuperclass());
        return classDescription;
    }

    @VisibleForTesting
    protected Set<GetterDescription> getterDescriptionsOf(Class<?> clazz) {
        return doGetterDescriptionsOf(getterMethodsOf(clazz), clazz);
    }

    @VisibleForTesting
    protected Set<GetterDescription> declaredGetterDescriptionsOf(Class<?> clazz) {
        return doGetterDescriptionsOf(declaredGetterMethodsOf(clazz), clazz);
    }

    private Set<GetterDescription> doGetterDescriptionsOf(Set<Method> getters, Class<?> clazz) {
        Set<GetterDescription> getterDescriptions = new TreeSet<GetterDescription>();
        for (Method getter : getters) {
            // ignore getDeclaringClass if Enum
            if (isGetDeclaringClassEnumGetter(getter, clazz))
                continue;
            final TypeDescription typeDescription = getTypeDescription(getter);
            final List<TypeName> exceptionTypeNames = getExceptionTypeNames(getter);
            String propertyName = propertyNameOf(getter);
            getterDescriptions.add(
                    new GetterDescription(propertyName, getter.getName(), typeDescription, exceptionTypeNames));
        }
        return getterDescriptions;
    }

    @VisibleForTesting
    protected Set<FieldDescription> declaredFieldDescriptionsOf(Class<?> clazz) {
        return doFieldDescriptionsOf(declaredPublicFieldsOf(clazz));
    }

    @VisibleForTesting
    protected Set<FieldDescription> fieldDescriptionsOf(Class<?> clazz) {
        return doFieldDescriptionsOf(nonStaticPublicFieldsOf(clazz));
    }

    private Set<FieldDescription> doFieldDescriptionsOf(List<Field> fields) {
        Set<FieldDescription> fieldDescriptions = new TreeSet<FieldDescription>();
        for (Field field : fields) {
            fieldDescriptions.add(new FieldDescription(field.getName(), getTypeDescription(field)));
        }
        return fieldDescriptions;
    }

    private boolean isGetDeclaringClassEnumGetter(final Method getter, final Class<?> clazz) {
        return clazz.isEnum() && getter.getName().equals("getDeclaringClass");
    }

    private List<TypeName> getExceptionTypeNames(final Method getter) {
        List<TypeName> exceptions = new ArrayList<TypeName>();
        for (Class<?> exception : getter.getExceptionTypes()) {
            exceptions.add(new TypeName(exception));
        }
        return exceptions;
    }

    private TypeDescription getTypeDescription(Member member) {
        final Class<?> type = getTypeOf(member);
        if (isArray(type))
            return buildArrayTypeDescription(type);
        // we are interested in collections and iterable but not subtype of Iterable that are not collection.
        // e.g. java.file.nio.Path that implements Iterable but has no ParameterizedType (ex : Path.getParent() -> Path)
        if (inheritsCollectionOrIsIterable(type))
            return buildIterableTypeDescription(member, type);
        // "simple" type
        return new TypeDescription(new TypeName(type));
    }

    private TypeDescription buildIterableTypeDescription(Member member, final Class<?> type) {
        final TypeDescription typeDescription = new TypeDescription(new TypeName(type));
        typeDescription.setIterable(true);
        if (methodReturnTypeHasNoParameterInfo(member)) {
            // not a ParameterizedType, i.e. no parameter information => use Object as element type.
            typeDescription.setElementTypeName(new TypeName(Object.class));
            return typeDescription;
        }
        ParameterizedType parameterizedType = getParameterizedTypeOf(member);
        if (parameterizedType.getActualTypeArguments()[0] instanceof GenericArrayType) {
            GenericArrayType genericArrayType = (GenericArrayType) parameterizedType.getActualTypeArguments()[0];
            typeDescription.setElementTypeName(new TypeName(genericArrayType.toString()));
            return typeDescription;
        }
        // Due to http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7151486,
        // java 7 is not able to detect GenericArrayType correctly => let's use a different way to detect array
        Class<?> internalClass = ClassUtil.getClass(parameterizedType.getActualTypeArguments()[0]);
        if (internalClass.isArray()) {
            String componentTypeWithoutClassPrefix = remove(internalClass.getComponentType().toString(), "class ");
            typeDescription.setElementTypeName(new TypeName(componentTypeWithoutClassPrefix + "[]"));
        } else {
            typeDescription.setElementTypeName(new TypeName(internalClass));
        }
        return typeDescription;
    }

    private static boolean methodReturnTypeHasNoParameterInfo(Member member) {
        // java loose generic info if getter is overriden :(
        return member instanceof Method && !(((Method) member).getGenericReturnType() instanceof ParameterizedType);
    }

    private static Class<?> getTypeOf(Member member) {
        if (member instanceof Method)
            return ((Method) member).getReturnType();
        if (member instanceof Field)
            return ((Field) member).getType();
        throw new IllegalArgumentException("argument should be a Method or Field but was " + member.getClass());
    }

    private static ParameterizedType getParameterizedTypeOf(Member member) {
        if (member instanceof Method)
            return (ParameterizedType) ((Method) member).getGenericReturnType();
        if (member instanceof Field)
            return (ParameterizedType) ((Field) member).getGenericType();
        throw new IllegalArgumentException("argument should be a Method or Field but was " + member.getClass());
    }

    private static TypeDescription buildArrayTypeDescription(final Class<?> arrayType) {
        final TypeDescription typeDescription = new TypeDescription(new TypeName(arrayType));
        typeDescription.setElementTypeName(new TypeName(arrayType.getComponentType()));
        typeDescription.setArray(true);
        return typeDescription;
    }

}