Package com.github.sviperll.adt4j

Source Code of com.github.sviperll.adt4j.JCodeModelJavaxLangModelAdapter

/*
* Copyright (c) 2014, Victor Nazarov <asviraspossible@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
*  1. Redistributions of source code must retain the above copyright notice,
*     this list of conditions and the following disclaimer.
*
*  2. Redistributions in binary form must reproduce the above copyright notice,
*     this list of conditions and the following disclaimer in the documentation and/or
*     other materials provided with the distribution.
*
*  3. Neither the name of the copyright holder nor the names of its contributors
*     may be used to endorse or promote products derived from this software
*     without specific prior written permission.
*
*  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
*  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
*  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
*  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
*  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
*  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
*   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
*  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
*  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
*  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.github.sviperll.adt4j;

import com.github.sviperll.adt4j.model.util.CodeGenerationException;
import com.github.sviperll.adt4j.model.util.ErrorTypeFound;
import com.github.sviperll.adt4j.model.util.ProcessingException;
import com.github.sviperll.adt4j.model.util.RuntimeProcessingException;
import com.helger.jcodemodel.AbstractJClass;
import com.helger.jcodemodel.AbstractJType;
import com.helger.jcodemodel.EClassType;
import com.helger.jcodemodel.IJAnnotatable;
import com.helger.jcodemodel.JAnnotationUse;
import com.helger.jcodemodel.JClassAlreadyExistsException;
import com.helger.jcodemodel.JCodeModel;
import com.helger.jcodemodel.JDefinedClass;
import com.helger.jcodemodel.JMethod;
import com.helger.jcodemodel.JMod;
import com.helger.jcodemodel.JTypeVar;
import com.helger.jcodemodel.JTypeWildcard;
import com.helger.jcodemodel.JVar;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.TypeParameterElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.ErrorType;
import javax.lang.model.type.ExecutableType;
import javax.lang.model.type.NoType;
import javax.lang.model.type.NullType;
import javax.lang.model.type.PrimitiveType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.TypeVariable;
import javax.lang.model.type.WildcardType;
import javax.lang.model.util.AbstractTypeVisitor6;

class JCodeModelJavaxLangModelAdapter {
    private static int toJMod(Collection<Modifier> modifierCollection) {
        int modifiers = 0;
        for (Modifier modifier: modifierCollection) {
            modifiers = modifiers | toJMod(modifier);
        }
        return modifiers;
    }

    private static int toJMod(Modifier modifier) {
        switch (modifier) {
            case ABSTRACT:
                return JMod.ABSTRACT;
            case FINAL:
                return JMod.FINAL;
            case NATIVE:
                return JMod.NATIVE;
            case PRIVATE:
                return JMod.PRIVATE;
            case PROTECTED:
                return JMod.PROTECTED;
            case PUBLIC:
                return JMod.PUBLIC;
            case STATIC:
                return JMod.STATIC;
            case SYNCHRONIZED:
                return JMod.SYNCHRONIZED;
            case TRANSIENT:
                return JMod.TRANSIENT;
            case VOLATILE:
                return JMod.VOLATILE;
            default:
                throw new UnsupportedOperationException("Unsupported modifier: " + modifier);
        }
    }

    private static EClassType toClassType(ElementKind kind) {
        switch (kind) {
            case CLASS:
                return EClassType.CLASS;
            case ENUM:
                return EClassType.ENUM;
            case INTERFACE:
                return EClassType.INTERFACE;
            case ANNOTATION_TYPE:
                return EClassType.ANNOTATION_TYPE_DECL;
            default:
                throw new UnsupportedOperationException("Unsupported ElementKind: " + kind);
        }
    }

    private final JCodeModel codeModel;

    JCodeModelJavaxLangModelAdapter(JCodeModel codeModel) {
        this.codeModel = codeModel;
    }

    JDefinedClass _class(TypeElement element) throws ProcessingException {
        return _class(element, new TypeEnvironment());
    }

    private JDefinedClass _class(TypeElement element, TypeEnvironment environment) throws ProcessingException {
        EClassType classType = toClassType(element.getKind());
        int modifiers = toJMod(element.getModifiers());
        if (classType.equals(EClassType.INTERFACE))
            modifiers = modifiers & ~JMod.ABSTRACT;

        JDefinedClass newClass;
        try {
            newClass = codeModel._class(modifiers, element.getQualifiedName().toString(), classType);
        } catch (JClassAlreadyExistsException ex) {
            throw new CodeGenerationException(ex);
        }
        newClass.hide();
        Annotator classAnnotator = new Annotator(newClass, environment);
        classAnnotator.annotate(element.getAnnotationMirrors());
        for (TypeParameterElement parameter: element.getTypeParameters()) {
            JTypeVar typeVariable = newClass.generify(parameter.getSimpleName().toString());
            environment.put(typeVariable.name(), typeVariable);
            for (TypeMirror type: parameter.getBounds()) {
                typeVariable.bound((AbstractJClass)toJType(type, environment));
            }
        }
        TypeMirror superclass = element.getSuperclass();
        if (superclass != null && superclass.getKind() != TypeKind.NONE) {
            newClass._extends((AbstractJClass)toJType(superclass, environment));
        }
        for (TypeMirror iface: element.getInterfaces()) {
            newClass._implements((AbstractJClass)toJType(iface, environment));
        }
        for (Element enclosedElement: element.getEnclosedElements()) {
            if (enclosedElement.getKind().equals(ElementKind.METHOD)) {
                ExecutableElement executable = (ExecutableElement)enclosedElement;
                JMethod method = newClass.method(toJMod(executable.getModifiers()), codeModel.VOID, executable.getSimpleName().toString());
                TypeEnvironment methodEnvironment = environment.enclosed();
                Annotator methodAnnotator = new Annotator(method, environment);
                methodAnnotator.annotate(executable.getAnnotationMirrors());
                for (TypeParameterElement parameter: executable.getTypeParameters()) {
                    JTypeVar typeVariable = method.generify(parameter.getSimpleName().toString());
                    methodEnvironment.put(typeVariable.name(), typeVariable);
                    for (TypeMirror type: parameter.getBounds()) {
                        typeVariable.bound((AbstractJClass)toJType(type, methodEnvironment));
                    }
                }
                method.type(toJType(executable.getReturnType(), methodEnvironment));
                for (TypeMirror type: executable.getThrownTypes()) {
                    AbstractJClass throwable = (AbstractJClass)toJType(type, methodEnvironment);
                    method._throws(throwable);
                }

                List<? extends VariableElement> parameters = executable.getParameters();
                int n = 0;
                for (VariableElement variable: parameters) {
                    String parameterName = variable.getSimpleName().toString();
                    TypeMirror parameterTypeMirror = variable.asType();
                    AbstractJType parameterType = toJType(parameterTypeMirror, methodEnvironment);

                    JVar param;
                    if (executable.isVarArgs() && n == parameters.size() - 1) {
                        param = method.varParam(toJMod(variable.getModifiers()), parameterType.elementType(), parameterName);
                    } else {
                        param = method.param(toJMod(variable.getModifiers()), parameterType, parameterName);
                    }
                    Annotator parametorAnnotator = new Annotator(param, methodEnvironment);
                    parametorAnnotator.annotate(variable.getAnnotationMirrors());
                    n++;
                }
            }
        }
        return newClass;
    }

    AbstractJClass ref(TypeElement element) throws ProcessingException {
        try {
            Class<?> klass = Class.forName(element.getQualifiedName().toString());
            AbstractJType declaredClass = codeModel.ref(klass);
            return (AbstractJClass)declaredClass;
        } catch (ClassNotFoundException ex) {
            AbstractJClass result = codeModel._getClass(element.getQualifiedName().toString());
            if (result != null)
                return result;
            else {
                JDefinedClass jclass = _class(element, new TypeEnvironment());
                jclass.hide();
                return jclass;
            }
        }
    }

    private AbstractJType toJType(TypeMirror type, final TypeEnvironment environment) throws ProcessingException {
        try {
            return type.accept(new AbstractTypeVisitor6<AbstractJType, Void>() {

                @Override
                public AbstractJType visitPrimitive(PrimitiveType t, Void p) {
                    switch (t.getKind()) {
                        case BOOLEAN:
                            return codeModel.BOOLEAN;
                        case BYTE:
                            return codeModel.BYTE;
                        case CHAR:
                            return codeModel.CHAR;
                        case INT:
                            return codeModel.INT;
                        case LONG:
                            return codeModel.LONG;
                        case FLOAT:
                            return codeModel.FLOAT;
                        case DOUBLE:
                            return codeModel.DOUBLE;
                        case SHORT:
                            return codeModel.SHORT;
                        default:
                            throw new IllegalArgumentException("Unrecognized primitive " + t.getKind());
                    }
                }

                @Override
                public AbstractJType visitNull(NullType t, Void p) {
                    throw new IllegalArgumentException("null can't be JClass."); //To change body of generated methods, choose Tools | Templates.
                }

                @Override
                public AbstractJType visitArray(ArrayType t, Void p) {
                    try {
                        AbstractJType componentType = toJType(t.getComponentType(), environment);
                        return componentType.array();
                    } catch (ProcessingException ex) {
                        throw new RuntimeProcessingException(ex);
                    }
                }

                @Override
                public AbstractJType visitDeclared(DeclaredType t, Void p) {
                    try {
                        TypeElement element = (TypeElement)t.asElement();
                        AbstractJClass _class = ref(element);
                        for (TypeMirror typeArgument: t.getTypeArguments()) {
                            _class = _class.narrow(toJType(typeArgument, environment));
                        }
                        return _class;
                    } catch (ProcessingException ex) {
                        throw new RuntimeProcessingException(ex);
                    }
                }

                @Override
                public AbstractJType visitError(ErrorType t, Void p) {
                    try {
                        throw new ErrorTypeFound();
                    } catch (ProcessingException ex) {
                        throw new RuntimeProcessingException(ex);
                    }
                }

                @Override
                public AbstractJType visitTypeVariable(TypeVariable t, Void p) {
                    return environment.get(t.asElement().getSimpleName().toString());
                }

                @Override
                public AbstractJType visitWildcard(WildcardType t, Void p) {
                    try {
                        TypeMirror extendsBoundMirror = t.getExtendsBound();
                        if (extendsBoundMirror != null) {
                            AbstractJClass extendsBound = (AbstractJClass)toJType(extendsBoundMirror, environment);
                            return extendsBound.wildcard(JTypeWildcard.EBoundMode.EXTENDS);
                        }
                        TypeMirror superBoundMirror = t.getSuperBound();
                        if (superBoundMirror != null) {
                            AbstractJClass superBound = (AbstractJClass)toJType(superBoundMirror, environment);
                            return superBound.wildcard(JTypeWildcard.EBoundMode.SUPER);
                        }
                        return codeModel.wildcard();
                    } catch (ProcessingException ex) {
                        throw new RuntimeProcessingException(ex);
                    }
                }

                @Override
                public AbstractJType visitExecutable(ExecutableType t, Void p) {
                    throw new IllegalArgumentException("executable can't be JClass."); //To change body of generated methods, choose Tools | Templates.
                }

                @Override
                public AbstractJType visitNoType(NoType t, Void p) {
                    throw new IllegalArgumentException("'no type' can't be JClass."); //To change body of generated methods, choose Tools | Templates.
                }

                @Override
                public AbstractJType visitUnknown(TypeMirror t, Void p) {
                    throw new IllegalArgumentException("unknown can't be JClass."); //To change body of generated methods, choose Tools | Templates.
                }
            }, null);
        } catch (RuntimeProcessingException ex) {
            throw ex.getCause();
        }
    }

    private class Annotator {
        private final IJAnnotatable annotatable;
        private final TypeEnvironment typeEnvironment;
        public Annotator(IJAnnotatable annotatable, TypeEnvironment typeEnvironment) {
            this.annotatable = annotatable;
            this.typeEnvironment = typeEnvironment;
        }

        private void annotate(List<? extends AnnotationMirror> annotationMirrors) throws ProcessingException {
            for (AnnotationMirror annotation: annotationMirrors) {
                JAnnotationUse annotationUse = annotatable.annotate((AbstractJClass)toJType(annotation.getAnnotationType(), typeEnvironment));
                for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> annotationValueAssignment: annotation.getElementValues().entrySet()) {
                    String name = annotationValueAssignment.getKey().getSimpleName().toString();
                    Object value = annotationValueAssignment.getValue().getValue();
                    if (value instanceof String)
                        annotationUse.param(name, (String)value);
                    else if (value instanceof Integer)
                        annotationUse.param(name, (Integer)value);
                    else if (value instanceof Long)
                        annotationUse.param(name, (Long)value);
                    else if (value instanceof Short)
                        annotationUse.param(name, (Short)value);
                    else if (value instanceof Float)
                        annotationUse.param(name, (Float)value);
                    else if (value instanceof Double)
                        annotationUse.param(name, (Double)value);
                    else if (value instanceof Byte)
                        annotationUse.param(name, (Byte)value);
                    else if (value instanceof Character)
                        annotationUse.param(name, (Character)value);
                    else if (value instanceof Class)
                        annotationUse.param(name, (Class)value);
                    else if (value instanceof Enum)
                        annotationUse.param(name, (Enum)value);
                    else if (value instanceof String[])
                        annotationUse.paramArray(name, (String[])value);
                    else if (value instanceof int[])
                        annotationUse.paramArray(name, (int[])value);
                    else if (value instanceof long[])
                        annotationUse.paramArray(name, (long[])value);
                    else if (value instanceof short[])
                        annotationUse.paramArray(name, (short[])value);
                    else if (value instanceof float[])
                        annotationUse.paramArray(name, (float[])value);
                    else if (value instanceof double[])
                        annotationUse.paramArray(name, (double[])value);
                    else if (value instanceof byte[])
                        annotationUse.paramArray(name, (byte[])value);
                    else if (value instanceof char[])
                        annotationUse.paramArray(name, (char[])value);
                    else if (value instanceof Class[])
                        annotationUse.paramArray(name, (Class[])value);
                    else if (value instanceof Enum[])
                        annotationUse.paramArray(name, (Enum[])value);
                }
            }
        }
    }
}
TOP

Related Classes of com.github.sviperll.adt4j.JCodeModelJavaxLangModelAdapter

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.