All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.gwtplatform.mvp.rebind.ClassInspector Maven / Gradle / Ivy

There is a newer version: 1.6
Show newest version
/**
 * Copyright 2011 ArcBees 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.gwtplatform.mvp.rebind;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JField;
import com.google.gwt.core.ext.typeinfo.JGenericType;
import com.google.gwt.core.ext.typeinfo.JMethod;
import com.google.gwt.core.ext.typeinfo.JParameterizedType;
import com.google.gwt.core.ext.typeinfo.JType;

/**
 * A helper class that can be used to inspect a class for methods having specific signatures.
 *
 * @author Philippe Beaudoin
 */
public class ClassInspector {

    private final TreeLogger logger;
    private final JClassType inspectedClass;

    ClassInspector(TreeLogger logger, JClassType inspectedClass) {
        this.logger = logger;
        this.inspectedClass = inspectedClass;
    }

    /**
     * Inspects the methods to find one that return the specified type. The method can be either in
     * the inspected class or any of its parent classes. It can be static or not.
     *
     * @param returnType The type that should be returned by the method.
     * @return The method returning {@code returnType}, or {@code null} if not found.
     * @throws UnableToCompleteException If more than one matching method is found.
     */
    public JMethod findMethodWithoutParamsReturning(JClassType returnType)
            throws UnableToCompleteException {
        JMethod result = null;
        for (JClassType classType : inspectedClass.getFlattenedSupertypeHierarchy()) {
            for (JMethod method : classType.getMethods()) {
                JClassType actualReturnType = method.getReturnType().isClassOrInterface();
                if (method.getParameters().length == 0
                        && actualReturnType != null
                        && returnType.isAssignableFrom(actualReturnType)) {
                    if (result != null) {
                        logger.log(TreeLogger.ERROR, "The class '" + inspectedClass.getName()
                                + "' has more than one method returning " + returnType.getName()
                                + " and taking no parameter. This is not allowed.", null);
                        throw new UnableToCompleteException();
                    }
                    result = method;
                }
            }
        }
        return result;
    }

    /**
     * Inspects the methods to find one that return the specified parameterized type with the
     * specified type as a parameter. The method can be either in the inspected class or any of its
     * parent classes. It can be static or not.
     *
     * @param returnType          The parameterized type that should be returned by the method.
     * @param returnTypeParameter The type parameter of {@code returnType}.
     * @return The method returning {@code returnType}, or {@code null} if not
     *         found.
     * @throws UnableToCompleteException If more than one matching method is found.
     */
    public JMethod findMethodWithoutParamsReturning(JGenericType returnType,
            JClassType returnTypeParameter) throws UnableToCompleteException {
        JMethod result = null;
        for (JClassType classType : inspectedClass.getFlattenedSupertypeHierarchy()) {
            for (JMethod method : classType.getMethods()) {
                JParameterizedType actualReturnType = method.getReturnType().isParameterized();
                if (method.getParameters().length == 0
                        && actualReturnType != null
                        && returnType.isAssignableFrom(actualReturnType)
                        && returnTypeParameter.isAssignableFrom(actualReturnType.getTypeArgs()[0])) {
                    if (result != null) {
                        logger.log(TreeLogger.ERROR, "The class '" + inspectedClass.getName()
                                + "' has more than one method returning " + returnType.getName()
                                + "<" + returnTypeParameter.getName() + "> and taking no parameter. "
                                + "This is not allowed.", null);
                        throw new UnableToCompleteException();
                    }
                    result = method;
                }
            }
        }
        return result;
    }

    /**
     * Inspects the methods to find one that return the specified type and is annotated with the
     * specified annotation. The method can be either in the inspected class or any of its parent
     * classes. It can be static or not.
     *
     * @param returnType The type that should be returned by the method.
     * @param annotation The annotation that should be present on the method.
     * @param failIfAnnotationIsFoundOnWrongMethod
     *                   If {@code true}, the call will throw an
     *                   {@link UnableToCompleteException} and log an error if the annotation is found on the
     *                   wrong method.
     * @return The method returning {@code returnType} and annotated with {@code annotation}, or
     *         {@code null} if not found.
     * @throws UnableToCompleteException If more than one matching method is found, or if the
     *                                   annotation is found on another method and {@code
     *                                   failIfAnnotationIsFoundOnWrongMethod}
     *                                   is {@code true}.
     */
    public JMethod findAnnotatedMethodWithoutParamsReturning(JClassType returnType,
            Class annotation,
            boolean failIfAnnotationIsFoundOnWrongMethod) throws UnableToCompleteException {
        JMethod result = null;
        for (JClassType classType : inspectedClass.getFlattenedSupertypeHierarchy()) {
            for (JMethod method : classType.getMethods()) {
                JClassType actualReturnType = method.getReturnType().isClassOrInterface();
                if (method.getAnnotation(annotation) != null) {
                    if (method.getParameters().length == 0
                            && actualReturnType != null
                            && returnType.isAssignableFrom(actualReturnType)) {
                        if (result != null) {
                            logger.log(TreeLogger.ERROR, "The class '" + inspectedClass.getName()
                                    + "' has more than one method returning " + returnType.getName()
                                    + " annotated with '" + annotation.getSimpleName() + "' "
                                    + " and taking no parameter. This is not allowed.", null);
                            throw new UnableToCompleteException();
                        }
                        result = method;
                    } else if (failIfAnnotationIsFoundOnWrongMethod) {
                        logger.log(TreeLogger.ERROR, "The class '" + inspectedClass.getName()
                                + "' has method '" + method.getName() + "' annotated with '"
                                + annotation.getSimpleName() + "', but the method has the wrong "
                                + "signature. It must take 0 parameter and return '"
                                + returnType.getName() + "'.", null);
                        throw new UnableToCompleteException();
                    }
                }
            }
        }
        return result;
    }

    /**
     * Inspects the methods to find one annotated with the specified annotation. The method can be
     * either in the inspected class or any of its parent classes. It can be static or not.
     *
     * @param annotation The annotation that should be present on the method.
     * @return The method annotated with {@code annotation}, or {@code null} if not found.
     * @throws UnableToCompleteException If more than one matching method is found.
     */
    public JMethod findAnnotatedMethod(Class annotation)
            throws UnableToCompleteException {
        JMethod result = null;
        for (JClassType classType : inspectedClass.getFlattenedSupertypeHierarchy()) {
            for (JMethod method : classType.getMethods()) {
                if (method.getAnnotation(annotation) != null) {
                    if (result != null) {
                        logger.log(TreeLogger.ERROR, "The class '" + inspectedClass.getName()
                                + "' has more than one method annotated with '"
                                + annotation.getSimpleName() + "'. This is not allowed.", null);
                        throw new UnableToCompleteException();
                    }
                    result = method;
                }
            }
        }
        return result;
    }

    /**
     * Inspects the class for a method with the given name and taking exactly one parameter of the
     * specified type, or of a subtype of that type.
     *
     * @param methodName    The name of the method to look for.
     * @param parameterType The type of the unique parameter accepted by that method.
     * @return The method, or {@code null} if not found.
     */
    public JMethod findMethod(String methodName, JClassType parameterType) {
        for (JClassType classType : inspectedClass.getFlattenedSupertypeHierarchy()) {
            for (JMethod method : classType.getMethods()) {
                if (methodName.equals(method.getName())) {
                    JType[] parameterTypes = method.getParameterTypes();
                    if (parameterTypes.length == 1) {
                        JClassType actualParameterType = parameterTypes[0].isClassOrInterface();
                        if (actualParameterType != null &&
                                parameterType.isAssignableFrom(actualParameterType)) {
                            return method;
                        }
                    }
                }
            }
        }
        return null;
    }

    /**
     * Collects the methods to find one annotated with the specified annotation. The method can be
     * either in the inspected class or any of its parent classes. It can be static or not.
     *
     * @param annotation The annotation that should be present on the methods.
     * @param collection The list in which to collect matching methods.
     */
    public void collectAnnotatedMethods(Class annotation,
            List collection) {
        for (JClassType classType : inspectedClass.getFlattenedSupertypeHierarchy()) {
            for (JMethod method : classType.getMethods()) {
                if (method.getAnnotation(annotation) != null) {
                    collection.add(method);
                }
            }
        }
    }

    /**
     * Collects all methods from either the inspected class or any of its parent classes.
     */
    public List getAllMethods() {
        List all = new ArrayList();
        for (JClassType classType : inspectedClass.getFlattenedSupertypeHierarchy()) {
            Collections.addAll(all, classType.getMethods());
        }
        return all;
    }

    /**
     * Inspects the fields to collect the ones of the specified parameterized type with the specified
     * type as a parameter annotated with the specified annotation. The field can be either in the
     * inspected class or any of its parent classes. Only static fields are collected.
     *
     * @param type          The parameterized type of the desired fields.
     * @param typeParameter The type parameter of {@code type}.
     * @param annotation    The annotation that should be present on the field.
     * @param collection    The list in which to collect matching fields.
     * @throws UnableToCompleteException If a field annotated with {@code annotation} is found that
     *                                   does not respect the other criteria of the search.
     */
    public void collectStaticAnnotatedFields(JClassType type,
            JClassType typeParameter, Class annotation,
            List collection) throws UnableToCompleteException {
        for (JClassType classType : inspectedClass.getFlattenedSupertypeHierarchy()) {
            for (JField field : classType.getFields()) {
                if (field.getAnnotation(annotation) != null) {
                    JParameterizedType parameterizedType = field.getType().isParameterized();
                    if (!field.isStatic()
                            || parameterizedType == null
                            || !type.isAssignableFrom(parameterizedType)
                            || !typeParameter.isAssignableFrom(parameterizedType.getTypeArgs()[0])) {
                        logger.log(
                                TreeLogger.ERROR, "Found the annotation @" + annotation.getSimpleName()
                                + " on the invalid field '" + classType.getName() + "." + field.getName()
                                + "'. Field must be static and its type must be " + type.getName()
                                + "<" + typeParameter.getName() + ">.", null);
                        throw new UnableToCompleteException();
                    }
                    collection.add(field);
                }
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy