Package booton.translator

Source Code of booton.translator.JavaMetadataCompiler

/*
* Copyright (C) 2013 Nameless Production Committee
*
* Licensed under the MIT License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*          http://opensource.org/licenses/mit-license.php
*/
package booton.translator;

import static booton.translator.Javascript.*;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.StringJoiner;

import kiss.I;

import org.junit.runner.RunWith;

import booton.Necessary;
import booton.Unnecessary;

/**
* @version 2013/10/10 9:13:49
*/
class JavaMetadataCompiler {

    /** The ignorable annotation classes. */
    private static final Set<Class<? extends Annotation>> ignorables = new HashSet();

    static {
        try {
            ignorables.add(JavaAPIProvider.class);
            ignorables.add(JavascriptAPIProvider.class);
            ignorables.add(JavascriptNativeProperty.class);
            ignorables.add(JavascriptNativePropertyAccessor.class);
            ignorables.add(RunWith.class);
            ignorables.add(Override.class);
            ignorables.add(Necessary.class);
            ignorables.add(Unnecessary.class);
        } catch (Exception e) {
            throw I.quiet(e);
        }
    }

    /** The code writer. */
    private final ScriptWriter code = new ScriptWriter();

    /**
     *
     */
    JavaMetadataCompiler(Class clazz) {
        List<Metadata> elements = new ArrayList();

        // class
        elements.add(new ClassMetadata(clazz));

        // constructors
        for (Constructor constructor : clazz.getDeclaredConstructors()) {
            elements.add(new ConstructorMetadata(constructor));
        }

        // fields
        for (Field field : clazz.getDeclaredFields()) {
            if (!field.getName().equals("serialVersionUID")) {
                elements.add(new FieldMetadata(field));
            }
        }

        // methods
        for (Method method : clazz.getDeclaredMethods()) {
            if (!TranslatorManager.isIgnorableMethod(method) && !method.isBridge() && !method.isSynthetic()) {
                elements.add(new MethodMetadata(method));
            }
        }

        // write metadata
        code.append("{");
        for (int i = 0; i < elements.size(); i++) {
            Metadata metadata = elements.get(i);
            code.append(metadata.name, ":", "[");
            metadata.defineMetadata();
            code.append(",");
            metadata.defineAnnotation();
            code.append("]").separator();
        }
        code.append("}");
    }

    /**
     * <p>
     * Compile annotation value.
     * </p>
     *
     * @param value
     */
    static String compileValue(Object value) {
        if (value == null) {
            return "null";
        }

        Class type = value.getClass();

        if (type == String.class || type == char.class || type == Character.class) {
            return "\"" + value + "\"";
        } else if (type == Class.class) {
            Javascript.require((Class) value);

            return Javascript.computeClass((Class) value);
        } else if (type.isEnum()) {
            Javascript.require(type);

            return Javascript.computeClassName(type) + "." + Javascript.computeFieldName(type, ((Enum) value).name());
        } else if (type.isArray()) {
            StringJoiner joiner = new StringJoiner(",", "[", "]");

            for (int i = 0; i < Array.getLength(value); i++) {
                joiner.add(compileValue(Array.get(value, i)));
            }
            return joiner.toString();
        } else if (type == Long.class) {
            return writePrimitiveCode((long) value);
        } else {
            return value.toString();
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {
        return code.toString();
    }

    /**
     * <p>
     * Cehck whether the spcified annotation set is valid or not.
     * </p>
     *
     * @param annotations
     * @return
     */
    private static boolean hasAnnotation(Annotation[][] annotations) {
        for (Annotation[] list : annotations) {
            if (hasAnnotation(list)) {
                return true;
            }
        }
        return false;
    }

    /**
     * <p>
     * Cehck whether the spcified annotation set is valid or not.
     * </p>
     *
     * @param annotations
     * @return
     */
    private static boolean hasAnnotation(Annotation[] annotations) {
        for (Annotation annotation : annotations) {
            if (!ignorables.contains(annotation.annotationType())) {
                return true;
            }
        }
        return false;
    }

    /**
     * @version 2013/05/29 19:49:06
     */
    private abstract class Metadata {

        /** The class, constructor, field or method name. */
        private final String name;

        /** The annotation. */
        private final AnnotatedElement element;

        /**
         * @param name A runtime name.
         * @param element An {@link AnnotatedElement}.
         */
        private Metadata(String name, AnnotatedElement element) {
            this.name = name;
            this.element = element;
        }

        /**
         *
         */
        protected abstract void defineMetadata();

        /**
         *
         */
        protected void defineAnnotation() {
            writeAnnotation(-1, element.getDeclaredAnnotations());
        }

        /**
         * <p>
         * Write annotaion definition.
         * </p>
         *
         * @param annotations
         */
        protected void writeAnnotation(Annotation[][] annotations) {
            if (hasAnnotation(annotations)) {
                code.append(",", "{");

                for (int i = 0; i < annotations.length; i++) {
                    writeAnnotation(i, annotations[i]);
                }
                code.append("}");
            }
        }

        /**
         * <p>
         * Write annotaion definition.
         * </p>
         *
         * @param annotations
         */
        protected void writeAnnotation(int index, Annotation[] annotations) {
            if (hasAnnotation(annotations)) {
                if (index < 0) {
                    code.append("{");
                } else {
                    code.append(index, ":{");
                }

                for (Annotation annotation : annotations) {
                    if (!ignorables.contains(annotation.annotationType())) {
                        Javascript.require(annotation.annotationType());

                        writeAnnotation(annotation);
                        code.separator();
                    }
                }
                code.append("}").separator();
            }
        }

        /**
         * <p>
         * Compile annotation to javascript.
         * </p>
         */
        protected void writeAnnotation(Annotation annotation) {
            Class type = annotation.annotationType();
            code.write(Javascript.computeSimpleClassName(type), ":");

            writeAnnotationValue(type, annotation);
        }

        /**
         * <p>
         * Compile annotation to javascript.
         * </p>
         */
        protected void writeAnnotationValue(Class type, Annotation annotation) {
            code.write("{");

            // collect annotation methods and compile to javascript expression
            for (Method method : type.getDeclaredMethods()) {
                method.setAccessible(true);

                try {
                    Object value = method.invoke(annotation);
                    Object defaultValue = method.getDefaultValue();

                    if (!Objects.equals(value, defaultValue)) {
                        code.write(Javascript.computeMethodName(method), ":", "function()", "{", "return ");

                        Class returnType = method.getReturnType();

                        if (returnType.isAnnotation()) {
                            writeAnnotationValue(returnType, (Annotation) value);
                        } else if (returnType.isArray() && returnType.getComponentType().isAnnotation()) {
                            code.write("[");

                            int size = Array.getLength(value);

                            for (int i = 0; i < size; i++) {
                                writeAnnotationValue(returnType.getComponentType(), (Annotation) Array.get(value, i));

                                if (i + 1 != size) {
                                    code.separator();
                                }
                            }
                            code.write("]");
                        } else {
                            code.write(compileValue(value));
                        }
                        code.write(";", "}").separator();
                    }
                } catch (Exception e) {
                    throw I.quiet(e);
                }
            }
            code.write("}");
        }
    }

    /**
     * @version 2013/09/07 10:21:55
     */
    private class ClassMetadata extends Metadata {

        /** The clazz . */
        private final Class clazz;

        /**
         * @param clazz
         */
        private ClassMetadata(Class clazz) {
            super("$", clazz);
            this.clazz = clazz;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        protected void defineMetadata() {
            int modifier = clazz.getModifiers();

            if (clazz.isMemberClass()) {
                modifier |= 0x00008000;
            }

            if (clazz.isAnonymousClass()) {
                modifier |= 0x00010000;
            }

            if (clazz.isLocalClass()) {
                modifier |= 0x00020000;
            }

            // code.append(modifier);
            code
                    .append(modifier, ",\"" + JavaAPIProviders.revert(clazz).getName() + "\"", ",", new JavaSignatureCompiler(clazz
                            .getTypeParameters()), ",", new JavaSignatureCompiler(clazz.getGenericSuperclass()), ",", new JavaSignatureCompiler(clazz
                            .getGenericInterfaces()));
        }
    }

    /**
     * @version 2013/09/03 22:50:57
     */
    private class FieldMetadata extends Metadata {

        /** The field . */
        private final Field field;

        /**
         * @param field
         */
        private FieldMetadata(Field field) {
            super(Javascript.computeFieldName(field), field);

            this.field = field;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        protected void defineMetadata() {
            code.append(field.getModifiers(), ",\"", field.getName(), "\",", new JavaSignatureCompiler(field
                    .getGenericType()));
        }
    }

    /**
     * @version 2013/10/10 9:13:44
     */
    private class MethodMetadata extends Metadata {

        /** The method . */
        private final Method method;

        /**
         * @param method
         */
        private MethodMetadata(Method method) {
            super(Javascript.computeMethodName(method), method);
            this.method = method;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        protected void defineMetadata() {
            String methodName = method.getName();

            if (methodName.startsWith("$alias$")) {
                methodName = methodName.substring(7);
            }

            code.append(method.getModifiers(), ",");
            code.append(new JavaSignatureCompiler(method.getTypeParameters()), ",");
            code.append(new JavaSignatureCompiler(method.getGenericParameterTypes()), ",");
            code.append(new JavaSignatureCompiler(method.getGenericExceptionTypes()), ",");
            code.append(new JavaSignatureCompiler(method.getGenericReturnType()), ",").string(methodName);
        }

        /**
         * {@inheritDoc}
         */
        @Override
        protected void defineAnnotation() {
            super.defineAnnotation();

            writeAnnotation(method.getParameterAnnotations());
        }
    }

    /**
     * @version 2013/05/12 13:35:36
     */
    private class ConstructorMetadata extends Metadata {

        /** The constructor . */
        private final Constructor constructor;

        /**
         * @param constructor
         */
        private ConstructorMetadata(Constructor constructor) {
            super(Javascript.computeMethodName(constructor), constructor);
            this.constructor = constructor;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        protected void defineMetadata() {
            code.append(constructor.getModifiers()).append(",");
            code.append(new JavaSignatureCompiler(constructor.getTypeParameters()), ",");
            code.append(new JavaSignatureCompiler(constructor.getGenericParameterTypes()), ",");
            code.append(new JavaSignatureCompiler(constructor.getGenericExceptionTypes()));
        }

        /**
         * {@inheritDoc}
         */
        @Override
        protected void defineAnnotation() {
            super.defineAnnotation();

            writeAnnotation(constructor.getParameterAnnotations());
        }
    }
}
TOP

Related Classes of booton.translator.JavaMetadataCompiler

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.