Example usage for java.lang.reflect Method getGenericReturnType

List of usage examples for java.lang.reflect Method getGenericReturnType

Introduction

In this page you can find the example usage for java.lang.reflect Method getGenericReturnType.

Prototype

public Type getGenericReturnType() 

Source Link

Document

Returns a Type object that represents the formal return type of the method represented by this Method object.

Usage

From source file:org.evosuite.setup.TestUsageChecker.java

public static boolean canUse(Method m, Class<?> ownerClass) {

    if (m.isBridge()) {
        logger.debug("Excluding bridge method: " + m.toString());
        return false;
    }/* w ww .j ava2  s  .c o m*/

    if (m.isSynthetic()) {
        logger.debug("Excluding synthetic method: " + m.toString());
        return false;
    }

    if (!Properties.USE_DEPRECATED && m.isAnnotationPresent(Deprecated.class)) {
        final Class<?> targetClass = Properties.getTargetClassAndDontInitialise();

        if (Properties.hasTargetClassBeenLoaded() && !m.getDeclaringClass().equals(targetClass)) {
            logger.debug("Excluding deprecated method " + m.getName());
            return false;
        }
    }

    if (m.isAnnotationPresent(Test.class) || m.isAnnotationPresent(Before.class)
            || m.isAnnotationPresent(BeforeClass.class) || m.isAnnotationPresent(After.class)
            || m.isAnnotationPresent(AfterClass.class)) {
        logger.debug("Excluding test method " + m.getName());
        return false;
    }

    if (m.isAnnotationPresent(EvoSuiteTest.class)) {
        logger.debug("Excluding EvoSuite test method " + m.getName());
        return false;
    }

    if (m.isAnnotationPresent(EvoSuiteExclude.class)) {
        logger.debug("Excluding method with exclusion annotation " + m.getName());
        return false;
    }

    if (m.getDeclaringClass().equals(java.lang.Object.class)) {
        return false;
    }

    if (!m.getReturnType().equals(String.class)
            && (!canUse(m.getReturnType()) || !canUse(m.getGenericReturnType()))) {
        return false;
    }

    for (java.lang.reflect.Type paramType : m.getGenericParameterTypes()) {
        if (!canUse(paramType))
            return false;
    }

    if (m.getDeclaringClass().equals(Enum.class)) {
        return false;
        /*
        if (m.getName().equals("valueOf") || m.getName().equals("values")
         || m.getName().equals("ordinal")) {
           logger.debug("Excluding valueOf for Enum " + m.toString());
           return false;
        }
        // Skip compareTo on enums (like Randoop)
        if (m.getName().equals("compareTo") && m.getParameterTypes().length == 1
         && m.getParameterTypes()[0].equals(Enum.class))
           return false;
           */
    }

    if (m.getDeclaringClass().equals(java.lang.Thread.class))
        return false;

    // Hashcode only if we need to cover it
    if (m.getName().equals("hashCode")) {
        final Class<?> targetClass = Properties.getTargetClassAndDontInitialise();

        if (!m.getDeclaringClass().equals(targetClass))
            return false;
        else {
            if (GraphPool.getInstance(ownerClass.getClassLoader()).getActualCFG(Properties.TARGET_CLASS,
                    m.getName() + Type.getMethodDescriptor(m)) == null) {
                // Don't cover generated hashCode
                // TODO: This should work via annotations
                return false;
            }
        }
    }

    // Randoop special case: just clumps together a bunch of hashCodes, so skip it
    if (m.getName().equals("deepHashCode") && m.getDeclaringClass().equals(Arrays.class))
        return false;

    // Randoop special case: differs too much between JDK installations
    if (m.getName().equals("getAvailableLocales"))
        return false;

    if (m.getName().equals(ClassResetter.STATIC_RESET)) {
        logger.debug("Ignoring static reset method");
        return false;
    }

    if (isForbiddenNonDeterministicCall(m)) {
        return false;
    }

    if (!Properties.CONSIDER_MAIN_METHODS && m.getName().equals("main") && Modifier.isStatic(m.getModifiers())
            && Modifier.isPublic(m.getModifiers())) {
        logger.debug("Ignoring static main method ");
        return false;
    }

    /*
    if(m.getTypeParameters().length > 0) {
       logger.debug("Cannot handle generic methods at this point");
       if(m.getDeclaringClass().equals(Properties.getTargetClass())) {
    LoggingUtils.getEvoLogger().info("* Skipping method "+m.getName()+": generic methods are not handled yet");
       }
       return false;
    }
    */

    // If default or
    if (Modifier.isPublic(m.getModifiers())) {
        TestClusterUtils.makeAccessible(m);
        return true;
    }

    // If default access rights, then check if this class is in the same package as the target class
    if (!Modifier.isPrivate(m.getModifiers())) {
        //              && !Modifier.isProtected(m.getModifiers())) {
        String packageName = ClassUtils.getPackageName(ownerClass);
        String declaredPackageName = ClassUtils.getPackageName(m.getDeclaringClass());
        if (packageName.equals(Properties.CLASS_PREFIX) && packageName.equals(declaredPackageName)) {
            TestClusterUtils.makeAccessible(m);
            return true;
        }
    }

    return false;
}

From source file:jp.terasoluna.fw.util.GenericPropertyUtilTest.java

/**
 * testResolveTypeClassClassTypeint08() <br>
 * <br>/*  ww w .  java 2s  . co m*/
 *  <br>
 * E <br>
 * <br>
 * () genericClass:List.class<br>
 * () clazz:ArrayList.class<br>
 * () type:List<String>?ParameterizedType<br>
 * () index:0<br>
 * <br>
 * () Class:String.class<br>
 * () :<br>
 * TRACE "Argument 'genericClass'(java.util.List) does not declare type parameter"<br>
 * <br>
 * type????????????? ???????? <br>
 * @throws Exception ?????
 */
@Test
public void testResolveTypeClassClassTypeint08() throws Exception {
    // ??
    Method method = GenericPropertyUtil_Stub01.class.getMethod("getList1", new Class[0]);
    Type type = method.getGenericReturnType();

    // 
    Class<?> actual = GenericPropertyUtil.resolveType(List.class, List.class, type, 0);
    // 
    assertEquals(String.class, actual);
    assertThat(logger.getLoggingEvents(), is(asList(
            trace("Argument " + "'genericClass'(java.util.List) does not " + "declare type parameter"))));
}

From source file:net.firejack.platform.core.store.AbstractStore.java

protected void findAlias(Class<?> clazz, String path, Map<String, String> aliases) {
    String[] fieldNames = path.split("\\.");
    if (fieldNames.length > 1) {
        for (int i = 0; i < fieldNames.length; i++) {
            String fieldName = fieldNames[i];
            PropertyDescriptor propertyDescriptor = ClassUtils.getPropertyDescriptor(clazz, fieldName);
            if (propertyDescriptor != null) {
                Method readMethod = propertyDescriptor.getReadMethod();
                if (readMethod != null) {
                    Class<?> returnType = readMethod.getReturnType();
                    if (Collection.class.isAssignableFrom(returnType)) {
                        returnType = (Class<?>) ((ParameterizedTypeImpl) readMethod.getGenericReturnType())
                                .getActualTypeArguments()[0];
                    }/*from   www  . ja  va  2 s.  co  m*/
                    if (AbstractModel.class.isAssignableFrom(returnType)) {
                        clazz = returnType;
                        if (i == 0) {
                            aliases.put(fieldName, fieldName);
                        } else {
                            aliases.put(fieldNames[i - 1] + "." + fieldName, fieldName);
                        }
                    }
                } else {
                    throw new BusinessFunctionException(
                            "The field '" + fieldName + "' has not read method in class '" + clazz + "'");
                }
            } else {
                throw new BusinessFunctionException(
                        "The field '" + fieldName + "' does not exist in class '" + clazz + "'");
            }
        }
    }
}

From source file:com.amazonaws.hal.client.HalResourceInvocationHandler.java

/**
 *///ww  w . jav a 2  s  . com
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    if (halResource == null || !halResource.isDefined()) {
        halResource = halClient.getHalResource(resourcePath);
    }

    try {
        Method resourceInfoMethod = ResourceInfo.class.getMethod(method.getName(), method.getParameterTypes());

        return resourceInfoMethod.invoke(halResource, args);
    } catch (NoSuchMethodException ignore) {
        // If the method is not defined in ResourceInfo, we handle it below
    } catch (InvocationTargetException e) {
        throw e.getTargetException();
    }

    Link link;
    if ((link = method.getAnnotation(Link.class)) != null) {
        switch (link.method()) {
        case GET:
            if (List.class.isAssignableFrom(method.getReturnType())) {
                //noinspection unchecked
                return new HalLinkList(halResource, link.relation(),
                        (Class) getCollectionType(method.getGenericReturnType(), 0, ResourceInfo.class),
                        halClient);
            } else if (Map.class.isAssignableFrom(method.getReturnType())) {
                //noinspection unchecked
                return new HalLinkMap(halResource, link.relation(), link.keyField(),
                        (Class) getCollectionType(method.getGenericReturnType(), 1, ResourceInfo.class),
                        halClient);
            } else {
                return halClient.getResource(halResource, method.getReturnType(), getRelationHref(link,
                        args == null ? EMPTY_ARGS : args, method.getParameterAnnotations()), false);
            }

        case POST:
            if (args == null) {
                throw new IllegalArgumentException("POST operations require a representation argument.");
            }

            return halClient.postResource(method.getReturnType(),
                    getRelationHref(link, args, method.getParameterAnnotations()), args[0]);

        case PUT:
            if (args == null) {
                throw new IllegalArgumentException("PUT operations require a representation argument.");
            }

            return halClient.putResource(method.getReturnType(),
                    getRelationHref(link, args, method.getParameterAnnotations()), args[0]);

        case DELETE:
            return halClient.deleteResource(method.getReturnType(),
                    getRelationHref(link, args == null ? EMPTY_ARGS : args, method.getParameterAnnotations()));

        default:
            throw new UnsupportedOperationException("Unexpected HTTP method: " + link.method());
        }

    } else if (method.getName().startsWith("get")) {
        String propertyName = getPropertyName(method.getName());
        Object property = halResource.getProperty(propertyName);
        Type returnType = method.getGenericReturnType();

        // When a value is accessed, it's intended type can either be a
        // class or some other type (like a ParameterizedType).
        //
        // If the target type is a class and the value is of that type,
        // we return it.  If the value is not of that type, we convert
        // it and store the converted value (trusting it was converted
        // properly) back to the backing store.
        //
        // If the target type is not a class, it may be ParameterizedType
        // like List<T> or Map<K, V>.  We check if the value is already
        // a converting type and if so, we return it.  If the value is
        // not, we convert it and if it's now a converting type, we store
        // the new value in the backing store.

        if (returnType instanceof Class) {
            if (!((Class) returnType).isInstance(property)) {
                property = convert(returnType, property);

                //noinspection unchecked
                halResource.addProperty(propertyName, property);
            }
        } else {
            if (!(property instanceof ConvertingMap) && !(property instanceof ConvertingList)) {
                property = convert(returnType, property);

                if (property instanceof ConvertingMap || property instanceof ConvertingList) {
                    //noinspection unchecked
                    halResource.addProperty(propertyName, property);
                }
            }
        }

        return property;
    } else if (method.getName().equals("toString") && args == null) {
        return resourcePath;
    } else if (method.getName().equals("equals") && args != null && args.length == 1) {
        HalResourceInvocationHandler other;

        try {
            other = (HalResourceInvocationHandler) Proxy.getInvocationHandler(args[0]);
        } catch (IllegalArgumentException e) {
            // argument is not a proxy
            return false;
        } catch (ClassCastException e) {
            // argument is the wrong type of proxy
            return false;
        }

        return resourcePath.equals(other.resourcePath);
    } else if (method.getName().equals("hashCode") && args == null) {
        return resourcePath.hashCode();
    }

    throw new UnsupportedOperationException("Don't know how to handle '" + method.getName() + "'");
}

From source file:com.msopentech.odatajclient.proxy.api.impl.EntityTypeInvocationHandler.java

@Override
@SuppressWarnings("unchecked")
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
    final Annotation[] methodAnnots = method.getAnnotations();

    if (isSelfMethod(method, args)) {
        return invokeSelfMethod(method, args);
    } else if (!ArrayUtils.isEmpty(methodAnnots) && methodAnnots[0] instanceof Operation) {
        final ODataOperation operation = this.entity.getOperation(((Operation) methodAnnots[0]).name());
        if (operation == null) {
            throw new IllegalArgumentException(
                    "Could not find any FunctionImport named " + ((Operation) methodAnnots[0]).name());
        }//from   w w  w. jav a2  s.c om

        final com.msopentech.odatajclient.engine.metadata.edm.v3.EntityContainer container = containerHandler
                .getFactory().getMetadata().getSchema(ClassUtils.getNamespace(typeRef))
                .getEntityContainer(entityContainerName);
        final com.msopentech.odatajclient.engine.metadata.edm.v3.FunctionImport funcImp = container
                .getFunctionImport(((Operation) methodAnnots[0]).name());

        return functionImport((Operation) methodAnnots[0], method, args, operation.getTarget(), funcImp);
    } // Assumption: for each getter will always exist a setter and viceversa.
    else if (method.getName().startsWith("get")) {
        // get method annotation and check if it exists as expected
        final Object res;

        final Method getter = typeRef.getMethod(method.getName());

        final Property property = ClassUtils.getAnnotation(Property.class, getter);
        if (property == null) {
            final NavigationProperty navProp = ClassUtils.getAnnotation(NavigationProperty.class, getter);
            if (navProp == null) {
                throw new UnsupportedOperationException("Unsupported method " + method.getName());
            } else {
                // if the getter refers to a navigation property ... navigate and follow link if necessary
                res = getNavigationPropertyValue(navProp, getter);
            }
        } else {
            // if the getter refers to a property .... get property from wrapped entity
            res = getPropertyValue(property, getter.getGenericReturnType());
        }

        // attach the current handler
        attach();

        return res;
    } else if (method.getName().startsWith("set")) {
        // get the corresponding getter method (see assumption above)
        final String getterName = method.getName().replaceFirst("set", "get");
        final Method getter = typeRef.getMethod(getterName);

        final Property property = ClassUtils.getAnnotation(Property.class, getter);
        if (property == null) {
            final NavigationProperty navProp = ClassUtils.getAnnotation(NavigationProperty.class, getter);
            if (navProp == null) {
                throw new UnsupportedOperationException("Unsupported method " + method.getName());
            } else {
                // if the getter refers to a navigation property ... 
                if (ArrayUtils.isEmpty(args) || args.length != 1) {
                    throw new IllegalArgumentException("Invalid argument");
                }

                setNavigationPropertyValue(navProp, args[0]);
            }
        } else {
            setPropertyValue(property, args[0]);
        }

        return ClassUtils.returnVoid();
    } else {
        throw new UnsupportedOperationException("Method not found: " + method);
    }
}

From source file:org.romaframework.core.schema.reflection.SchemaClassReflection.java

public Boolean initGetterForField(Method method, ParameterizedType owner) {
    int prefixLength;
    String fieldName = method.getName();
    if (fieldName.startsWith(GET_METHOD) && checkIfFirstCharAfterPrefixIsUpperCase(fieldName, GET_METHOD))
        prefixLength = GET_METHOD.length();
    else if (fieldName.startsWith(IS_METHOD) && checkIfFirstCharAfterPrefixIsUpperCase(fieldName, IS_METHOD))
        prefixLength = IS_METHOD.length();
    else//from w  ww.j  ava  2  s . c  o  m
        return false;
    if (method.getParameterTypes() != null && method.getParameterTypes().length > 0)
        return false;
    if (fieldName.length() <= prefixLength)
        return false;

    fieldName = firstToLower(fieldName.substring(prefixLength));

    Class<?> javaFieldClass = SchemaHelper.resolveClassFromType(method.getGenericReturnType(), owner);

    SchemaFieldReflection fieldInfo = (SchemaFieldReflection) getField(fieldName);
    if (fieldInfo == null) {
        fieldInfo = createField(fieldName, javaFieldClass);
        fieldInfo.getterMethod = method;
    } else if (fieldInfo instanceof SchemaFieldReflection) {
        if (fieldInfo instanceof SchemaFieldDelegate
                && !((SchemaFieldReflection) fieldInfo).getLanguageType().isAssignableFrom(javaFieldClass)) {
            fieldInfo = createField(fieldName, javaFieldClass);
            fieldInfo.getterMethod = method;
        } else {
            fieldInfo.getterMethod = method;
            if (!((SchemaFieldReflection) fieldInfo).getLanguageType().isAssignableFrom(javaFieldClass)) {
                fieldInfo.setterMethod = null;
            }
        }
    }

    return true;
}

From source file:org.gradle.model.internal.manage.schema.store.ModelSchemaExtractor.java

public <T> ExtractedModelSchema<T> extract(ModelType<T> type) {
    validateType(type);/*from w  w w  . j  a v  a 2s  .c o  m*/

    List<Method> methodList = Arrays.asList(type.getRawClass().getDeclaredMethods());
    if (methodList.isEmpty()) {
        return new ExtractedModelSchema<T>(type, Collections.<ModelPropertyFactory<?>>emptyList());
    }

    List<ModelPropertyFactory<?>> propertyFactories = Lists.newLinkedList();

    Map<String, Method> methods = Maps.newHashMap();
    for (Method method : methodList) {
        String name = method.getName();
        if (methods.containsKey(name)) {
            throw invalidMethod(type, name, "overloaded methods are not supported");
        }
        methods.put(name, method);
    }

    List<String> methodNames = Lists.newLinkedList(methods.keySet());
    List<String> handled = Lists.newArrayList();

    for (ListIterator<String> iterator = methodNames.listIterator(); iterator.hasNext();) {
        String methodName = iterator.next();

        Method method = methods.get(methodName);
        if (methodName.startsWith("get") && !methodName.equals("get")) {
            if (method.getParameterTypes().length != 0) {
                throw invalidMethod(type, methodName, "getter methods cannot take parameters");
            }

            Character getterPropertyNameFirstChar = methodName.charAt(3);
            if (!Character.isUpperCase(getterPropertyNameFirstChar)) {
                throw invalidMethod(type, methodName,
                        "the 4th character of the getter method name must be an uppercase character");
            }

            ModelType<?> returnType = ModelType.of(method.getGenericReturnType());
            if (isManaged(returnType.getRawClass())) {
                propertyFactories
                        .add(extractPropertyOfManagedType(type, methods, methodName, returnType, handled));
            } else {
                propertyFactories
                        .add(extractPropertyOfUnmanagedType(type, methods, methodName, returnType, handled));
            }
            iterator.remove();
        }
    }

    methodNames.removeAll(handled);

    // TODO - should call out valid getters without setters
    if (!methodNames.isEmpty()) {
        throw invalid(type, "only paired getter/setter methods are supported (invalid methods: ["
                + Joiner.on(", ").join(methodNames) + "])");
    }

    return new ExtractedModelSchema<T>(type, propertyFactories);
}

From source file:com.github.reinert.jjschema.JsonSchemaGenerator.java

protected <T> ObjectNode generatePropertySchema(Class<T> type, Method method, Field field)
        throws TypeException {
    Class<?> returnType = method != null ? method.getReturnType() : field.getType();

    AccessibleObject propertyReflection = field != null ? field : method;

    SchemaIgnore ignoreAnn = propertyReflection.getAnnotation(SchemaIgnore.class);
    if (ignoreAnn != null)
        return null;

    ObjectNode schema = createInstance();

    JsonManagedReference refAnn = propertyReflection.getAnnotation(JsonManagedReference.class);
    if (refAnn != null) {
        ManagedReference fowardReference;
        Class<?> genericClass;
        Class<?> collectionClass;
        if (Collection.class.isAssignableFrom(returnType)) {
            if (method != null) {
                ParameterizedType genericType = (ParameterizedType) method.getGenericReturnType();
                genericClass = (Class<?>) genericType.getActualTypeArguments()[0];
            } else {
                genericClass = field.getClass();
            }/*  ww  w  .  j a  va 2 s . c o m*/
            collectionClass = returnType;
        } else {
            genericClass = returnType;
        }
        fowardReference = new ManagedReference(type, refAnn.value(), genericClass);

        if (!isFowardReferencePiled(fowardReference)) {
            pushFowardReference(fowardReference);
        } else
        //           if (isBackwardReferencePiled(fowardReference)) 
        {
            boolean a = pullFowardReference(fowardReference);
            boolean b = pullBackwardReference(fowardReference);
            //return null;
            return createRefSchema("#");
        }
    }

    JsonBackReference backRefAnn = propertyReflection.getAnnotation(JsonBackReference.class);
    if (backRefAnn != null) {
        ManagedReference backReference;
        Class<?> genericClass;
        Class<?> collectionClass;
        if (Collection.class.isAssignableFrom(returnType)) {
            ParameterizedType genericType = (ParameterizedType) method.getGenericReturnType();
            genericClass = (Class<?>) genericType.getActualTypeArguments()[0];
            collectionClass = returnType;
        } else {
            genericClass = returnType;
        }
        backReference = new ManagedReference(genericClass, backRefAnn.value(), type);

        if (isFowardReferencePiled(backReference) && !isBackwardReferencePiled(backReference)) {
            pushBackwardReference(backReference);
        } else {
            //              pullFowardReference(backReference);
            //              pullBackwardReference(backReference);
            return null;
        }
    }

    if (Collection.class.isAssignableFrom(returnType)) {
        processPropertyCollection(method, field, schema);
    } else {
        schema = generateSchema(returnType);
    }

    // Check the field annotations, if the get method references a field, or the
    // method annotations on the other hand, and processSchemaProperty them to
    // the JsonSchema object
    Attributes attrs = propertyReflection.getAnnotation(Attributes.class);
    if (attrs != null) {
        processSchemaProperty(schema, attrs);
        // The declaration of $schema is only necessary at the root object
        schema.remove("$schema");
    }

    // Check if the Nullable annotation is present, and if so, add 'null' to type attr
    Nullable nullable = propertyReflection.getAnnotation(Nullable.class);
    if (nullable != null) {
        if (returnType.isEnum()) {
            ((ArrayNode) schema.get("enum")).add("null");
        } else {
            String oldType = schema.get(TAG_TYPE).asText();
            ArrayNode typeArray = schema.putArray(TAG_TYPE);
            typeArray.add(oldType);
            typeArray.add("null");
        }
    }

    return schema;
}

From source file:org.apache.servicecomb.swagger.engine.SwaggerEnvironment.java

public SwaggerProducer createProducer(Object producerInstance, Class<?> swaggerIntf,
        Map<String, Operation> swaggerOperationMap) {
    Class<?> producerCls = BeanUtils.getImplClassFromBean(producerInstance);
    Map<String, Method> visibleProducerMethods = retrieveVisibleMethods(producerCls);

    SwaggerProducer producer = new SwaggerProducer();
    producer.setProducerCls(producerCls);
    producer.setSwaggerIntf(swaggerIntf);
    for (Method swaggerMethod : swaggerIntf.getMethods()) {
        String methodName = swaggerMethod.getName();
        // producer??swagger?
        Method producerMethod = visibleProducerMethods.getOrDefault(methodName, null);
        if (producerMethod == null) {
            // producer?
            String msg = String.format("swagger method %s not exist in producer %s.", methodName,
                    producerInstance.getClass().getName());
            throw new Error(msg);
        }//from   w w  w . ja v a 2  s  .  co  m

        ArgumentsMapperConfig config = new ArgumentsMapperConfig();
        config.setSwaggerMethod(swaggerMethod);
        config.setProviderMethod(producerMethod);
        config.setSwaggerOperation(swaggerOperationMap.get(methodName));
        config.setSwaggerGeneratorContext(compositeSwaggerGeneratorContext.selectContext(producerCls));

        ProducerArgumentsMapperFactory argumentsMapperFactory = selectProducerArgumentsMapperFactory(config);
        ProducerArgumentsMapper argsMapper = argumentsMapperFactory.createArgumentsMapper(config);
        ProducerResponseMapper responseMapper = producerResponseMapperFactorys.createResponseMapper(
                swaggerMethod.getGenericReturnType(), producerMethod.getGenericReturnType());

        SwaggerProducerOperation op = new SwaggerProducerOperation();
        op.setName(methodName);
        op.setProducerClass(producerCls);
        op.setProducerInstance(producerInstance);
        op.setProducerMethod(producerMethod);
        op.setSwaggerMethod(swaggerMethod);
        op.setArgumentsMapper(argsMapper);
        op.setResponseMapper(responseMapper);

        producer.addOperation(op);
    }

    return producer;
}

From source file:com.expedia.tesla.compiler.plugins.JavaTypeMapper.java

/**
 * Generate Tesla schema from Java class by reflection.
 * <p>/*from w  ww. j av a 2 s. c  o m*/
 * Tesla can generate schema from existing Java classes that follow the
 * JavaBeans Spec. Only properties with following attributes will be
 * included:
 * <li>public accessible.</li>
 * <li>writable (has both getter and setter).</li>
 * <li>has no {@code SkipField} annotation.</li>
 * <p>
 * Tesla will map Java type to it's closest Tesla type by default.
 * Developers can override this by either providing their own
 * {@code TypeMapper}, or with Tesla annoations.
 * <p>
 * Tesla will map all Java object types to nullable types only for class
 * properties. If you want an property to be not nullable, use annotation
 * {@code NotNullable}.
 * 
 * @param schemaBuilder
 *            All non-primitive Tesla types must be defined inside a schema.
 *            This is the schema object into which the Tesla type will be
 *            generated.
 * 
 * @param javaType
 *            The java class object.
 * 
 * @return The Tesla type created from the java class by reflection.
 * 
 * @throws TeslaSchemaException
 */
public Type fromJavaClass(Schema.SchemaBuilder schemaBuilder, java.lang.Class<?> javaType)
        throws TeslaSchemaException {
    String className = javaType.getCanonicalName();
    if (className == null) {
        throw new TeslaSchemaException(
                String.format("Tesla cannot generate schema for local class '%s'.", javaType.getName()));
    }
    String classTypeId = Class.nameToId(className);
    Class clss = (Class) schemaBuilder.findType(classTypeId);
    if (clss != null) {
        return clss;
    }

    clss = (Class) schemaBuilder.addType(classTypeId);

    Class superClass = null;
    java.lang.Class<?> base = javaType.getSuperclass();
    if (base != null && base != java.lang.Object.class) {
        superClass = (Class) fromJavaClass(schemaBuilder, javaType.getSuperclass());
    }

    List<Field> fields = new ArrayList<>();
    for (PropertyDescriptor propDesc : PropertyUtils.getPropertyDescriptors(javaType)) {
        Type fieldType = null;
        String fieldName = propDesc.getName();
        Method readMethod = propDesc.getReadMethod();
        Method writeMethod = propDesc.getWriteMethod();

        // Ignore the property it missing getter or setter method.
        if (writeMethod == null || readMethod == null) {
            continue;
        }
        if ((superClass != null && superClass.hasField(fieldName)) || clss.hasField(fieldName)) {
            continue;
        }
        // Ignore the property if it is annotated with "SkipField".
        if (readMethod.getAnnotation(com.expedia.tesla.schema.annotation.SkipField.class) != null) {
            continue;
        }
        com.expedia.tesla.schema.annotation.TypeId tidAnnotation = readMethod
                .getAnnotation(com.expedia.tesla.schema.annotation.TypeId.class);

        String typeId = null;
        if (tidAnnotation != null) {
            typeId = tidAnnotation.value();
        }
        java.lang.reflect.Type propType = readMethod.getGenericReturnType();
        fieldType = fromJava(schemaBuilder, propType);
        if (typeId != null) {
            fieldType = schemaBuilder.addType(typeId);
        } else {
            if (!(propType instanceof java.lang.Class<?> && ((java.lang.Class<?>) propType).isPrimitive())) {
                fieldType = schemaBuilder.addType(String.format("nullable<%s>", fieldType.getTypeId()));
            }
            com.expedia.tesla.schema.annotation.NotNullable anntNotNullable = readMethod
                    .getAnnotation(com.expedia.tesla.schema.annotation.NotNullable.class);
            com.expedia.tesla.schema.annotation.Nullable anntNullable = readMethod
                    .getAnnotation(com.expedia.tesla.schema.annotation.Nullable.class);
            if (anntNotNullable != null && anntNullable != null) {
                throw new TeslaSchemaException(String.format(
                        "Property '%' of class '%s' has conflict annotations." + "'NotNullable' and 'Nullable'",
                        fieldName));
            }
            if (fieldType.isNullable() && anntNotNullable != null) {
                fieldType = ((Nullable) fieldType).getElementType();
            }
            if (!fieldType.isReference()
                    && readMethod.getAnnotation(com.expedia.tesla.schema.annotation.Reference.class) != null) {
                fieldType = schemaBuilder.addType(String.format("reference<%s>", fieldType.getTypeId()));
            }
        }

        com.expedia.tesla.schema.annotation.FieldName fnAnnotation = readMethod
                .getAnnotation(com.expedia.tesla.schema.annotation.FieldName.class);
        if (fnAnnotation != null) {
            fieldName = fnAnnotation.value();
        }

        String fieldDisplayName = propDesc.getDisplayName();
        String getter = readMethod.getName();
        String setter = propDesc.getWriteMethod().getName();
        com.expedia.tesla.schema.annotation.DisplayName dnAnnotation = readMethod
                .getAnnotation(com.expedia.tesla.schema.annotation.DisplayName.class);
        if (dnAnnotation != null) {
            fieldDisplayName = dnAnnotation.value();
        }
        java.util.Map<String, String> attributes = new java.util.HashMap<String, String>();
        attributes.put("getter", getter);
        attributes.put("setter", setter);

        fields.add(new Field(fieldName, fieldDisplayName, fieldType, attributes, null));
    }

    clss.define(superClass == null ? null : Arrays.asList(new Class[] { superClass }), fields, null);
    return clss;
}