Package org.jboss.gwt.circuit.processor

Source Code of org.jboss.gwt.circuit.processor.GenerationUtil

/*
* JBoss, Home of Professional Open Source.
* Copyright 2010, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.gwt.circuit.processor;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;

final class GenerationUtil {

    private GenerationUtil() {}

    static final String OPT_DISPATCHER_NAME = "dispatcherName";
    static final String OPT_DISPATCHER_PACKAGE = "dispatcherPackage";
    static final String DEFAULT_DISPATCHER_NAME = "ApplicationDispatcher";

    /**
     * Handy constant for an emtpy array of argument types.
     */
    static final String[] NO_PARAMS = new String[0];

    /**
     * Passing a reference to exactly this array causes
     * {@link #getAnnotatedMethods(javax.lang.model.element.TypeElement, javax.annotation.processing.ProcessingEnvironment,
     * String, javax.lang.model.type.TypeMirror, String[], StringBuilder)} not to care about parameter types.
     */
    static final String[] ANY_PARAMS = new String[0];

    static String storeImplementation(String delegate) {
        return delegate + "Adapter";
    }

    static boolean isInstanceOf(final TypeElement clazz, final String className) {
        boolean match = clazz.getQualifiedName().toString().equals(className);
        if (!match) {
            TypeMirror superclass = clazz.getSuperclass();
        }
        return false;
    }

    /**
     * Finds all public, non-static, no-args method annotated with the given annotation which returns the given type.
     * <p/>
     * If a method with the given annotation is found but the method does not satisfy the requirements listed above,
     * the
     * method will be marked with an error explaining the problem. This will trigger a compilation failure.
     * <p/>
     * If more than one method satisfies all the criteria, all such methods are marked with an error explaining the
     * problem.
     *
     * @param originalClassElement   the class to search for the annotated method.
     * @param processingEnvironment  the current annotation processing environment.
     * @param annotationName         the fully-qualified name of the annotation to search for.
     * @param requiredReturnType     the fully qualified name of the type the method must return.
     * @param requiredParameterTypes the parameter types the method must take. If the method must take no parameters,
     *                               use
     *                               {@link #NO_PARAMS}. If the method can take any parameters, use {@link
     *                               #ANY_PARAMS}.
     * @param errorHolder            filled with an error, if the given criteria do not match.
     *
     * @return a list of references to the methods that satisfy the criteria (empty list if no such method exists).
     */
    static List<ExecutableElement> getAnnotatedMethods(final TypeElement originalClassElement,
            final ProcessingEnvironment processingEnvironment, final String annotationName,
            final TypeMirror requiredReturnType, final String[] requiredParameterTypes, StringBuilder errorHolder) {

        final Types typeUtils = processingEnvironment.getTypeUtils();
        final Elements elementUtils = processingEnvironment.getElementUtils();

        TypeElement classElement = originalClassElement;
        while (true) {
            final List<ExecutableElement> methods = ElementFilter.methodsIn(classElement.getEnclosedElements());

            List<ExecutableElement> matches = new ArrayList<>();
            for (ExecutableElement e : methods) {

                final TypeMirror actualReturnType = e.getReturnType();

                if (getAnnotation(elementUtils, e, annotationName) == null) {
                    continue;
                }

                List<String> problems = new ArrayList<>();

                if (!typeUtils.isAssignable(actualReturnType, requiredReturnType)) {
                    problems.add("return " + requiredReturnType);
                }
                if (!doParametersMatch(typeUtils, elementUtils, e, requiredParameterTypes)) {
                    if (requiredParameterTypes.length == 0) {
                        problems.add("take no parameters");
                    } else {
                        StringBuilder sb = new StringBuilder();
                        sb.append("take ").append(requiredParameterTypes.length).append(" parameters of type (");
                        boolean first = true;
                        for (String p : requiredParameterTypes) {
                            if (!first) {
                                sb.append(", ");
                            }
                            sb.append(p);
                            first = false;
                        }
                        sb.append(")");
                        problems.add(sb.toString());
                    }
                }
                if (e.getModifiers().contains(Modifier.STATIC)) {
                    problems.add("be non-static");
                }
                if (e.getModifiers().contains(Modifier.PRIVATE)) {
                    problems.add("be non-private");
                }


                if (problems.isEmpty()) {
                    matches.add(e);
                } else {
                    if (errorHolder != null) {
                        errorHolder.append(formatProblemsList(annotationName, problems));
                    }
                }
            }

            if (!matches.isEmpty()) {
                return matches;
            }

            TypeMirror superclass = classElement.getSuperclass();
            if (superclass instanceof DeclaredType) {
                classElement = (TypeElement) ((DeclaredType) superclass).asElement();
            } else {
                break;
            }
        }

        return Collections.emptyList();
    }

    static AnnotationMirror getAnnotation(Elements elementUtils, Element annotationTarget,
            String annotationName) {
        for (AnnotationMirror annotation : elementUtils.getAllAnnotationMirrors(annotationTarget)) {
            if (annotationName.contentEquals(getQualifiedName(annotation))) {
                return annotation;
            }
        }
        return null;
    }

    static Name getQualifiedName(AnnotationMirror annotation) {
        return ((TypeElement) annotation.getAnnotationType().asElement()).getQualifiedName();
    }

    static Collection<String> extractValue(final AnnotationValue value) {
        if (value.getValue() instanceof Collection) {
            final Collection<?> varray = (List<?>) value.getValue();
            final ArrayList<String> result = new ArrayList<String>(varray.size());
            for (final Object active : varray) {
                result.addAll(extractValue((AnnotationValue) active));
            }
            return result;
        }
        return Collections.singleton(value.getValue().toString());
    }

    /**
     * Checks whether the ExecutableElement's parameter list matches the requiredParameterTypes (order matters).
     *
     * @param typeUtils              type utils from current processing environment.
     * @param elementUtils           element utils from current processing environment.
     * @param e                      the method whose parameter list to check.
     * @param requiredParameterTypes the required parameter types. Must not be null.
     *                               If a reference to {@link #ANY_PARAMS}, this method returns true without any
     *                               further
     *                               checks.
     *
     * @return true if the target method's parameter list matches the given required parameter types, or if the special
     * {@link #ANY_PARAMS} value is passed as {@code requiredParameterTypes}. False otherwise.
     */
    static boolean doParametersMatch(final Types typeUtils,
            final Elements elementUtils,
            final ExecutableElement e,
            final String[] requiredParameterTypes) {
        if (requiredParameterTypes == ANY_PARAMS) {
            return true;
        }
        if (e.getParameters().size() != requiredParameterTypes.length) {
            return false;
        }
        List<TypeMirror> requiredTypes = new ArrayList<>();
        for (String parameterType : requiredParameterTypes) {
            requiredTypes.add(elementUtils.getTypeElement(parameterType).asType());
        }
        for (int i = 0; i < requiredTypes.size(); i++) {
            final TypeMirror actualType = e.getParameters().get(i).asType();
            final TypeMirror requiredType = requiredTypes.get(i);
            if (!typeUtils.isAssignable(actualType,
                    requiredType)) {
                return false;
            }
        }
        return true;
    }

    /**
     * Renders the given list of problems with an annotated method as an English sentence.
     * The sentence takes the form "Methods annotated with <i>annotationSimpleName</i> must <i>list of problems</i>".
     * Commas and "and" are inserted as appropriate.
     *
     * @param annotationFqcn the fully-qualified name of the annotation the problems pertain to.
     * @param problems       the list of problems, as verb phrases. Must not be null, and should contain at least one
     *                       item.
     *
     * @return a nice English sentence summarizing the problems.
     */
    private static String formatProblemsList(final String annotationFqcn, List<String> problems) {
        StringBuilder msg = new StringBuilder();
        msg.append("Methods annotated with @")
                .append(fqcnToSimpleName(annotationFqcn))
                .append(" must ");
        for (int i = 0; i < problems.size(); i++) {
            if (problems.size() > 2 && i > 0) {
                msg.append(", ");
            }
            if (problems.size() == 2 && i == 1) {
                msg.append(" and ");
            }
            if (problems.size() > 2 && i == problems.size() - 1) {
                msg.append("and ");
            }
            msg.append(problems.get(i));
        }
        return msg.toString();
    }

    private static String fqcnToSimpleName(String fqcn) {
        int lastIndexOfDot = fqcn.lastIndexOf('.');
        if (lastIndexOfDot != -1) {
            return fqcn.substring(lastIndexOfDot + 1);
        }
        return fqcn;
    }
}
TOP

Related Classes of org.jboss.gwt.circuit.processor.GenerationUtil

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.