com.google.devtools.j2objc.translate.RuntimeAnnotationGenerator.java Source code

Java tutorial

Introduction

Here is the source code for com.google.devtools.j2objc.translate.RuntimeAnnotationGenerator.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.
 */

package com.google.devtools.j2objc.translate;

import com.google.common.collect.Iterables;
import com.google.devtools.j2objc.ast.AbstractTypeDeclaration;
import com.google.devtools.j2objc.ast.Annotation;
import com.google.devtools.j2objc.ast.ArrayCreation;
import com.google.devtools.j2objc.ast.ArrayInitializer;
import com.google.devtools.j2objc.ast.Block;
import com.google.devtools.j2objc.ast.BodyDeclaration;
import com.google.devtools.j2objc.ast.CompilationUnit;
import com.google.devtools.j2objc.ast.Expression;
import com.google.devtools.j2objc.ast.FunctionDeclaration;
import com.google.devtools.j2objc.ast.FunctionInvocation;
import com.google.devtools.j2objc.ast.MethodDeclaration;
import com.google.devtools.j2objc.ast.NullLiteral;
import com.google.devtools.j2objc.ast.ReturnStatement;
import com.google.devtools.j2objc.ast.SimpleName;
import com.google.devtools.j2objc.ast.SingleVariableDeclaration;
import com.google.devtools.j2objc.ast.TreeUtil;
import com.google.devtools.j2objc.ast.TypeLiteral;
import com.google.devtools.j2objc.types.FunctionBinding;
import com.google.devtools.j2objc.types.GeneratedTypeBinding;
import com.google.devtools.j2objc.types.Types;
import com.google.devtools.j2objc.util.BindingUtil;
import com.google.devtools.j2objc.util.NameTable;

import org.eclipse.jdt.core.dom.IAnnotationBinding;
import org.eclipse.jdt.core.dom.IMemberValuePairBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.Modifier;

import java.util.ArrayList;
import java.util.List;

/**
 * Utility class that generates the functions that return runtime annotations.
 */
class RuntimeAnnotationGenerator {

    private static final GeneratedTypeBinding ANNOTATION_TYPE = GeneratedTypeBinding
            .newTypeBinding("java.lang.annotation.Annotation", null, true);

    private final AbstractTypeDeclaration typeNode;
    private final Types typeEnv;
    private final NameTable nameTable;
    private final String className;

    private int funcCount = 0;

    public RuntimeAnnotationGenerator(AbstractTypeDeclaration typeNode) {
        this.typeNode = typeNode;
        CompilationUnit unit = TreeUtil.getCompilationUnit(typeNode);
        typeEnv = unit.getTypeEnv();
        nameTable = unit.getNameTable();
        className = nameTable.getFullName(typeNode.getTypeBinding());
    }

    /**
     * Generate a function that returns the annotations for a BodyDeclarations node.
     */
    public String createFunction(BodyDeclaration decl) {
        List<Annotation> runtimeAnnotations = TreeUtil.getRuntimeAnnotationsList(decl.getAnnotations());
        if (runtimeAnnotations.isEmpty()) {
            return null;
        }
        return addFunction(createAnnotations(runtimeAnnotations));
    }

    /**
     * Generate a function that returns the 2-dimentional array of annotations for method parameters.
     */
    public String createParamsFunction(MethodDeclaration method) {
        List<SingleVariableDeclaration> params = method.getParameters();

        // Quick test to see if there are any parameter annotations.
        boolean hasAnnotations = false;
        for (SingleVariableDeclaration param : params) {
            if (!Iterables.isEmpty(TreeUtil.getRuntimeAnnotations(param.getAnnotations()))) {
                hasAnnotations = true;
                break;
            }
        }
        if (!hasAnnotations) {
            return null;
        }

        List<Expression> subArrays = new ArrayList<>();
        for (SingleVariableDeclaration param : params) {
            List<Annotation> runtimeAnnotations = TreeUtil.getRuntimeAnnotationsList(param.getAnnotations());
            if (runtimeAnnotations.isEmpty()) {
                subArrays.add(new ArrayCreation(GeneratedTypeBinding.newArrayType(ANNOTATION_TYPE), typeEnv, 0));
            } else {
                subArrays.add(createAnnotations(runtimeAnnotations));
            }
        }

        return addFunction(createObjectArray(subArrays, GeneratedTypeBinding.newArrayType(ANNOTATION_TYPE)));
    }

    private String addFunction(Expression result) {
        String name = className + "__Annotations$" + funcCount++;
        FunctionDeclaration decl = new FunctionDeclaration(name, result.getTypeBinding(),
                typeNode.getTypeBinding());
        decl.addModifiers(Modifier.PRIVATE);
        Block body = new Block();
        decl.setBody(body);
        body.addStatement(new ReturnStatement(result));
        typeNode.addBodyDeclaration(decl);
        return name;
    }

    private Expression createAnnotations(List<Annotation> annotations) {
        List<Expression> expressions = new ArrayList<>();
        for (Annotation annotation : annotations) {
            expressions.add(createAnnotation(annotation.getAnnotationBinding()));
        }
        return createObjectArray(expressions, ANNOTATION_TYPE);
    }

    private Expression createObjectArray(List<Expression> expressions, ITypeBinding componentType) {
        ITypeBinding arrayType = GeneratedTypeBinding.newArrayType(componentType);
        ArrayCreation creation = new ArrayCreation(arrayType, typeEnv);
        ArrayInitializer initializer = new ArrayInitializer(arrayType);
        initializer.getExpressions().addAll(expressions);
        creation.setInitializer(initializer);
        return creation;
    }

    private Expression createAnnotation(IAnnotationBinding annotationBinding) {
        ITypeBinding annotationType = annotationBinding.getAnnotationType();
        FunctionBinding binding = new FunctionBinding("create_" + nameTable.getFullName(annotationType),
                annotationType, annotationType);
        FunctionInvocation invocation = new FunctionInvocation(binding, annotationType);
        for (IMemberValuePairBinding valueBinding : BindingUtil.getSortedMemberValuePairs(annotationBinding)) {
            binding.addParameters(valueBinding.getMethodBinding().getReturnType());
            invocation.addArgument(createAnnotationValue(valueBinding.getValue()));
        }
        return invocation;
    }

    private Expression createAnnotationValue(Object value) {
        if (value == null) {
            return new NullLiteral();
        } else if (value instanceof IVariableBinding) {
            return new SimpleName((IVariableBinding) value);
        } else if (value instanceof ITypeBinding) {
            return new TypeLiteral((ITypeBinding) value, typeEnv);
        } else if (value instanceof IAnnotationBinding) {
            return createAnnotation((IAnnotationBinding) value);
        } else if (value instanceof Object[]) {
            Object[] array = (Object[]) value;
            List<Expression> generatedValues = new ArrayList<>();
            for (Object elem : array) {
                generatedValues.add(createAnnotationValue(elem));
            }
            return createObjectArray(generatedValues, typeEnv.getNSObject());
        } else { // Boolean, Character, Number, String
            return TreeUtil.newLiteral(value, typeEnv);
        }
    }
}