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

Java tutorial

Introduction

Here is the source code for com.google.devtools.j2objc.gen.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.gen;

import com.google.common.collect.Iterables;
import com.google.devtools.j2objc.Options;
import com.google.devtools.j2objc.ast.AbstractTypeDeclaration;
import com.google.devtools.j2objc.ast.Annotation;
import com.google.devtools.j2objc.ast.FieldDeclaration;
import com.google.devtools.j2objc.ast.MethodDeclaration;
import com.google.devtools.j2objc.ast.PackageDeclaration;
import com.google.devtools.j2objc.ast.SingleVariableDeclaration;
import com.google.devtools.j2objc.ast.TreeUtil;
import com.google.devtools.j2objc.ast.VariableDeclarationFragment;
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.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;

import java.util.List;

/**
 * Generates the accessor methods used by JRE reflection code to get the runtime
 * annotations for types, methods and fields.
 *
 * @author Tom Ball, Keith Stanger
 */
public class RuntimeAnnotationGenerator extends AbstractSourceGenerator {

    private final NameTable nameTable;

    private RuntimeAnnotationGenerator(SourceBuilder builder, NameTable nameTable) {
        super(builder);
        this.nameTable = nameTable;
    }

    public static void printPackageAnnotationMethod(SourceBuilder builder, PackageDeclaration node) {
        new RuntimeAnnotationGenerator(builder, TreeUtil.getCompilationUnit(node).getNameTable())
                .printPackageAnnotationMethod(node);
    }

    public static void printTypeAnnotationMethods(SourceBuilder builder, AbstractTypeDeclaration node) {
        RuntimeAnnotationGenerator annotationGen = new RuntimeAnnotationGenerator(builder,
                TreeUtil.getCompilationUnit(node).getNameTable());
        annotationGen.printTypeAnnotationsMethod(node);
        annotationGen.printMethodAnnotationMethods(TreeUtil.getMethodDeclarations(node));
        annotationGen.printFieldAnnotationMethods(node);
    }

    public void printPackageAnnotationMethod(PackageDeclaration node) {
        List<Annotation> runtimeAnnotations = TreeUtil.getRuntimeAnnotationsList(node.getAnnotations());
        println("\n+ (IOSObjectArray *)__annotations {");
        printAnnotationCreate(runtimeAnnotations);
    }

    public void printTypeAnnotationsMethod(AbstractTypeDeclaration decl) {
        List<Annotation> runtimeAnnotations = TreeUtil.getRuntimeAnnotationsList(decl.getAnnotations());
        if (runtimeAnnotations.size() > 0) {
            println("\n+ (IOSObjectArray *)__annotations {");
            printAnnotationCreate(runtimeAnnotations);
        }
    }

    public void printMethodAnnotationMethods(Iterable<MethodDeclaration> methods) {
        for (MethodDeclaration method : methods) {
            List<Annotation> runtimeAnnotations = TreeUtil.getRuntimeAnnotationsList(method.getAnnotations());
            if (runtimeAnnotations.size() > 0) {
                printf("\n+ (IOSObjectArray *)__annotations_%s {\n", methodKey(method.getMethodBinding()));
                printAnnotationCreate(runtimeAnnotations);
            }
            printParameterAnnotationMethods(method);
        }
    }

    private void printParameterAnnotationMethods(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) {
            // Print array of arrays, with an element in the outer array for each parameter.
            printf("\n+ (IOSObjectArray *)__annotations_%s_params {\n", methodKey(method.getMethodBinding()));
            print("  return [IOSObjectArray arrayWithObjects:(id[]) { ");
            for (int i = 0; i < params.size(); i++) {
                if (i > 0) {
                    print(", ");
                }
                SingleVariableDeclaration param = params.get(i);
                List<Annotation> runtimeAnnotations = TreeUtil.getRuntimeAnnotationsList(param.getAnnotations());
                if (runtimeAnnotations.size() > 0) {
                    print("[IOSObjectArray arrayWithObjects:(id[]) { ");
                    printAnnotations(runtimeAnnotations);
                    printf(" } count:%d type:JavaLangAnnotationAnnotation_class_()]", runtimeAnnotations.size());
                } else {
                    print("[IOSObjectArray arrayWithLength:0 type:JavaLangAnnotationAnnotation_class_()]");
                }
            }
            printf(" } count:%d type:IOSClass_arrayOf(" + "JavaLangAnnotationAnnotation_class_())];\n}\n",
                    params.size());
        }
    }

    public void printFieldAnnotationMethods(AbstractTypeDeclaration node) {
        for (FieldDeclaration field : TreeUtil.getFieldDeclarations(node)) {
            List<Annotation> runtimeAnnotations = TreeUtil.getRuntimeAnnotationsList(field.getAnnotations());
            if (!runtimeAnnotations.isEmpty()) {
                for (VariableDeclarationFragment var : field.getFragments()) {
                    printf("\n+ (IOSObjectArray *)__annotations_%s_ {\n", var.getName().getIdentifier());
                    printAnnotationCreate(runtimeAnnotations);
                }
            }
        }
    }

    private String parameterKey(IMethodBinding method) {
        StringBuilder sb = new StringBuilder();
        ITypeBinding[] parameterTypes = method.getParameterTypes();
        for (int i = 0; i < parameterTypes.length; i++) {
            if (i == 0) {
                sb.append(NameTable.capitalize(nameTable.parameterKeyword(parameterTypes[i])));
            } else {
                sb.append(nameTable.parameterKeyword(parameterTypes[i]));
            }
            sb.append('_');
        }
        return sb.toString();
    }

    private String methodKey(IMethodBinding method) {
        StringBuilder sb = new StringBuilder(NameTable.getMethodName(method));
        sb.append(parameterKey(method));
        return sb.toString();
    }

    private void printAnnotationCreate(List<Annotation> runtimeAnnotations) {
        print("  return [IOSObjectArray arrayWithObjects:(id[]) { ");
        printAnnotations(runtimeAnnotations);
        printf(" } count:%d type:JavaLangAnnotationAnnotation_class_()];\n}\n", runtimeAnnotations.size());
    }

    private void printAnnotations(Iterable<Annotation> runtimeAnnotations) {
        boolean first = true;
        for (Annotation annotation : runtimeAnnotations) {
            if (first) {
                first = false;
            } else {
                print(", ");
            }
            printAnnotation(annotation.getAnnotationBinding());
        }
    }

    private void printAnnotation(IAnnotationBinding annotation) {
        if (Options.useReferenceCounting()) {
            print('[');
        }
        printf("[[%s alloc] init", nameTable.getFullName(annotation.getAnnotationType()));
        printAnnotationParameters(annotation);
        print(']');
        if (Options.useReferenceCounting()) {
            print(" autorelease]");
        }
    }

    // Prints an annotation's values as a constructor argument list. If
    // the annotation type declares default values, then for any value that
    // isn't specified in the annotation will use the default.
    private void printAnnotationParameters(IAnnotationBinding annotation) {
        IMemberValuePairBinding[] valueBindings = BindingUtil.getSortedMemberValuePairs(annotation);
        for (int i = 0; i < valueBindings.length; i++) {
            if (i > 0) {
                print(' ');
            }
            IMemberValuePairBinding valueBinding = valueBindings[i];
            print(i == 0 ? "With" : "with");
            printf("%s:",
                    NameTable.capitalize(NameTable.getAnnotationPropertyName(valueBinding.getMethodBinding())));
            Object value = valueBinding.getValue();
            printAnnotationValue(value);
        }
    }

    private void printAnnotationValue(Object value) {
        if (value == null) {
            print("nil");
        } else if (value instanceof IVariableBinding) {
            IVariableBinding var = (IVariableBinding) value;
            ITypeBinding declaringClass = var.getDeclaringClass();
            printf("%s_get_%s()", nameTable.getFullName(declaringClass), var.getName());
        } else if (value instanceof ITypeBinding) {
            printf("%s_class_()", nameTable.getFullName((ITypeBinding) value));
        } else if (value instanceof String) {
            print(LiteralGenerator.generateStringLiteral((String) value));
        } else if (value instanceof Number || value instanceof Character || value instanceof Boolean) {
            print(value.toString());
        } else if (value.getClass().isArray()) {
            print("[IOSObjectArray arrayWithObjects:(id[]) { ");
            Object[] array = (Object[]) value;
            for (int i = 0; i < array.length; i++) {
                if (i > 0) {
                    print(", ");
                }
                printAnnotationValue(array[i]);
            }
            printf(" } count:%d type:NSObject_class_()]", array.length);
        } else if (value instanceof IAnnotationBinding) {
            printAnnotation((IAnnotationBinding) value);
        } else {
            assert false : "unknown annotation value type";
        }
    }
}