All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.sundr.codegen.functions.Sources Maven / Gradle / Ivy

There is a newer version: 1.14.0
Show newest version
/*
 * Copyright 2016 The original authors.
 *
 *    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 io.sundr.codegen.functions;

import com.github.javaparser.JavaParser;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.ImportDeclaration;
import com.github.javaparser.ast.NamedNode;
import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.TypeParameter;
import com.github.javaparser.ast.body.AnnotationDeclaration;
import com.github.javaparser.ast.body.AnnotationMemberDeclaration;
import com.github.javaparser.ast.body.BodyDeclaration;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.ConstructorDeclaration;
import com.github.javaparser.ast.body.FieldDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.body.Parameter;
import com.github.javaparser.ast.body.TypeDeclaration;
import com.github.javaparser.ast.body.VariableDeclarator;
import com.github.javaparser.ast.expr.AnnotationExpr;
import com.github.javaparser.ast.expr.NameExpr;
import com.github.javaparser.ast.expr.QualifiedNameExpr;
import com.github.javaparser.ast.stmt.BlockStmt;
import com.github.javaparser.ast.stmt.Statement;
import com.github.javaparser.ast.type.ClassOrInterfaceType;
import com.github.javaparser.ast.type.PrimitiveType;
import com.github.javaparser.ast.type.ReferenceType;
import com.github.javaparser.ast.type.Type;
import com.github.javaparser.ast.type.VoidType;
import com.github.javaparser.ast.type.WildcardType;
import io.sundr.builder.Function;
import io.sundr.codegen.DefinitionRepository;
import io.sundr.codegen.model.AnnotationRef;
import io.sundr.codegen.model.AnnotationRefBuilder;
import io.sundr.codegen.model.AttributeKey;
import io.sundr.codegen.model.Attributeable;
import io.sundr.codegen.model.Block;
import io.sundr.codegen.model.BlockBuilder;
import io.sundr.codegen.model.ClassRef;
import io.sundr.codegen.model.ClassRefBuilder;
import io.sundr.codegen.model.Kind;
import io.sundr.codegen.model.Method;
import io.sundr.codegen.model.MethodBuilder;
import io.sundr.codegen.model.PrimitiveRef;
import io.sundr.codegen.model.PrimitiveRefBuilder;
import io.sundr.codegen.model.Property;
import io.sundr.codegen.model.PropertyBuilder;
import io.sundr.codegen.model.StringStatement;
import io.sundr.codegen.model.StringStatementBuilder;
import io.sundr.codegen.model.TypeDef;
import io.sundr.codegen.model.TypeDefBuilder;
import io.sundr.codegen.model.TypeParamDef;
import io.sundr.codegen.model.TypeParamDefBuilder;
import io.sundr.codegen.model.TypeParamRef;
import io.sundr.codegen.model.TypeParamRefBuilder;
import io.sundr.codegen.model.TypeRef;
import io.sundr.codegen.model.VoidRef;
import io.sundr.codegen.model.WildcardRef;
import io.sundr.codegen.model.WildcardRefBuilder;
import io.sundr.codegen.utils.IOUtils;
import io.sundr.codegen.utils.TypeUtils;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class Sources {

    private static final String JAVA_LANG = "java.lang";
    private static final String SEPARATOR = ".";

    private static Function PACKAGENAME = new Function() {

        public String apply(Node node) {
            if (node instanceof NamedNode) {
                String name = ((NamedNode)node).getName();
                Node current = node;
                while (!(current instanceof CompilationUnit)) {
                    current = current.getParentNode();
                }

                CompilationUnit compilationUnit = (CompilationUnit) current;

                for (ImportDeclaration importDecl : compilationUnit.getImports()) {
                    NameExpr importExpr = importDecl.getName();
                    if (importExpr instanceof QualifiedNameExpr) {
                        QualifiedNameExpr qualifiedNameExpr = (QualifiedNameExpr) importExpr;
                        String className = qualifiedNameExpr.getName();
                        if (name.equals(className)) {
                            return qualifiedNameExpr.getQualifier().toString();
                        }
                    } else if (importDecl.getName().getName().endsWith(SEPARATOR + name)) {
                        String importName = importDecl.getName().getName();
                        return  importName.substring(0, importName.length() - name.length() -1);
                    }
                }

               try {
                   Class.forName(JAVA_LANG + "." + name);
                   return JAVA_LANG;
               } catch (ClassNotFoundException ex) {
                   return compilationUnit.getPackage().getPackageName();
               }
            }
            return null;
        }
    };


    private static Function> IMPORTS = new Function>() {

        public Set apply(Node node) {
            Set imports = new LinkedHashSet();

            if (node instanceof NamedNode) {
                String name = ((NamedNode)node).getName();
                Node current = node;
                while (!(current instanceof CompilationUnit)) {
                    current = current.getParentNode();
                }

                CompilationUnit compilationUnit = (CompilationUnit) current;

                for (ImportDeclaration importDecl : compilationUnit.getImports()) {
                    String className = null;
                    String packageName = null;

                    NameExpr importExpr = importDecl.getName();
                    if (importExpr instanceof QualifiedNameExpr) {
                        QualifiedNameExpr qualifiedNameExpr = (QualifiedNameExpr) importExpr;
                        className = qualifiedNameExpr.getName();
                        packageName = qualifiedNameExpr.getQualifier().toString();
                    } else if (importDecl.getName().getName().endsWith(SEPARATOR + name)) {
                        String importName = importDecl.getName().getName();
                        packageName = importName.substring(0, importName.length() - name.length() -1);
                    }
                    if (className != null && !className.isEmpty()) {
                        imports.add(new ClassRefBuilder().withNewDefinition().withName(className).withPackageName(packageName).and().build());
                    }
                }
            }
            return imports;
        }
    };

    private static Function CLASS_OR_TYPEPARAM_REF = new Function() {
        public TypeRef apply(ClassOrInterfaceType classOrInterfaceType) {
            String boundPackage = PACKAGENAME.apply(classOrInterfaceType);
            String boundName = classOrInterfaceType.getName();

            List arguments = new ArrayList();
            for (Type arg :classOrInterfaceType.getTypeArgs()) {
                if (arg instanceof ReferenceType) {
                    //TODO: Need to check if this is valid for all cases...
                    ReferenceType referenceType = (ReferenceType) arg;
                    Type type = referenceType.getType();
                    int dimensions = referenceType.getArrayCount();
                    if (type instanceof ClassOrInterfaceType) {
                        TypeRef intermediateRef = CLASS_OR_TYPEPARAM_REF.apply((ClassOrInterfaceType)type);
                        if (intermediateRef instanceof ClassRef) {
                            arguments.add(new ClassRefBuilder((ClassRef) intermediateRef)
                                    .withDimensions(dimensions)
                                    .build());
                        } else if (intermediateRef instanceof TypeParamRef) {
                            arguments.add(new TypeParamRefBuilder((TypeParamRef) intermediateRef)
                                    .withDimensions(dimensions)
                                    .build());
                        } else {
                            throw new IllegalStateException("Expected class or type param reference");
                        }
                    } else {
                        String name = referenceType.toString();
                        arguments.add(new TypeParamRefBuilder()
                                .withName(name)
                                .withDimensions(dimensions)
                                .build());
                    }
                } else if (arg instanceof WildcardType) {
                    WildcardType wildcardType = (WildcardType)arg;
                    if (wildcardType.getExtends() != null) {
                        TypeRef bound = TYPEREF.apply(wildcardType.getExtends());
                        arguments.add(new WildcardRefBuilder().addToBounds(bound).build());
                    } else {
                        arguments.add(new WildcardRef());
                    }
                }
            }


            if (classOrInterfaceType.getParentNode()  == classOrInterfaceType) {
                return new TypeParamRefBuilder().withName(boundName).build();
            }

            String fqn = boundPackage + "." + boundName;
            TypeDef knownDefinition = DefinitionRepository.getRepository().getDefinition(fqn);

            if (knownDefinition != null) {
                return arguments.isEmpty()
                        ? new ClassRefBuilder().withDefinition(knownDefinition).build()
                        : knownDefinition.toReference(arguments);
            } else if (classOrInterfaceType.getTypeArgs().isEmpty() && boundName.length() == 1)  {
                //We are doing our best here to distinguish between class refs and type parameter refs.
                return new TypeParamRefBuilder().withName(boundName).build();
            } else {
                return new ClassRefBuilder()
                        .withNewDefinition()
                        .withPackageName(boundPackage)
                        .withName(boundName)
                        .endDefinition()
                        .withArguments(arguments)
                        .build();
            }
        }
    };


    private static Function TYPEREF = new Function() {
        public TypeRef apply(Type type) {
            if (type instanceof VoidType) {
                return new VoidRef();
            } else if (type instanceof WildcardType) {
                return new WildcardRef();
            } else if (type instanceof ReferenceType) {
                ReferenceType referenceType = (ReferenceType) type;
                int dimensions = referenceType.getArrayCount();
                TypeRef typeRef = TYPEREF.apply(referenceType.getType());
                if (dimensions == 0) {
                    return typeRef;
                } else if (typeRef instanceof ClassRef) {
                    return new ClassRefBuilder((ClassRef)typeRef).withDimensions(dimensions).build();
                } else if (typeRef instanceof PrimitiveRef) {
                    return new PrimitiveRefBuilder((PrimitiveRef)typeRef).withDimensions(dimensions).build();
                } else if (typeRef instanceof TypeParamRef) {
                    return new TypeParamRefBuilder((TypeParamRef)typeRef).withDimensions(dimensions).build();
                }
            } else if (type instanceof PrimitiveType) {
                PrimitiveType primitiveType = (PrimitiveType) type;
                return new PrimitiveRefBuilder().withName(primitiveType.getType().name()).build();
            } else if (type instanceof ClassOrInterfaceType) {
                return CLASS_OR_TYPEPARAM_REF.apply((ClassOrInterfaceType) type);
            }
            throw new IllegalArgumentException("Can't handle type:[" + type + "].");
        }
    };

    private static Function TYPEPARAMDEF = new Function() {

        public TypeParamDef apply(TypeParameter typeParameter) {
            List bounds = new ArrayList();
            for (ClassOrInterfaceType classOrInterfaceType : typeParameter.getTypeBound()) {
                bounds.add((ClassRef) CLASS_OR_TYPEPARAM_REF.apply(classOrInterfaceType));
            }
            return new TypeParamDefBuilder()
                    .withName(typeParameter.getName())
                    .withBounds(bounds)
                    .build();
        }
    };

    private static Function ANNOTATIONREF = new Function() {

        public AnnotationRef apply(AnnotationExpr annotation) {
            String name = annotation.getName().getName();
            String packageName = PACKAGENAME.apply(annotation);
            return new AnnotationRefBuilder()
                    .withNewClassRef()
                        .withNewDefinition()
                            .withName(name)
                            .withPackageName(packageName)
                        .endDefinition()
                    .endClassRef()
                    .build();
        }
    };


    private static final Function STATEMENT = new Function() {
        public StringStatement apply(Statement stmt) {
            return new StringStatementBuilder().withProvider(() -> stmt.toString()).build();
        }
    };

    private static final Function BLOCK = new Function() {
        public Block apply(BlockStmt block) {
            List statements = new ArrayList();
            if (block != null) {
                for (Statement stmt : block.getStmts()) {
                    statements.add(STATEMENT.apply(stmt));
                }
            }
            return new BlockBuilder().withStatements(statements).build();
        }
    };

    public static Function TYPEDEF = new Function() {

        public TypeDef apply(TypeDeclaration type) {
            if (type instanceof ClassOrInterfaceDeclaration) {
                ClassOrInterfaceDeclaration decl = (ClassOrInterfaceDeclaration) type;
                Kind kind = decl.isInterface() ? Kind.INTERFACE : Kind.CLASS;

                List parameters = new ArrayList();

                List extendsList = new ArrayList();
                List implementsList = new ArrayList();
                List properties = new ArrayList();
                List methods = new ArrayList();
                List constructors = new ArrayList();
                List annotations = new ArrayList();

                for (AnnotationExpr annotationExpr : decl.getAnnotations()) {
                    annotations.add(ANNOTATIONREF.apply(annotationExpr));
                }

                for (TypeParameter typeParameter: decl.getTypeParameters()) {
                    parameters.add(TYPEPARAMDEF.apply(typeParameter));
                }

                for (ClassOrInterfaceType classOrInterfaceType : decl.getExtends()) {
                    extendsList.add((ClassRef) CLASS_OR_TYPEPARAM_REF.apply(classOrInterfaceType));
                }

                for (ClassOrInterfaceType classOrInterfaceType : decl.getImplements()) {
                    implementsList.add((ClassRef) CLASS_OR_TYPEPARAM_REF.apply(classOrInterfaceType));
                }

                for (BodyDeclaration bodyDeclaration : decl.getMembers()) {
                    if (bodyDeclaration instanceof FieldDeclaration) {
                        FieldDeclaration fieldDeclaration = (FieldDeclaration) bodyDeclaration;
                        for (VariableDeclarator var : fieldDeclaration.getVariables()) {
                            TypeRef typeRef = checkAgainstTypeParamRef(TYPEREF.apply(fieldDeclaration.getType()), parameters);
                            properties.add(new PropertyBuilder()
                                    .withName(var.getId().getName())
                                    .withTypeRef(typeRef)
                                    .withModifiers(fieldDeclaration.getModifiers())
                                    .addToAttributes(Attributeable.INIT,  var.getInit() != null ? var.getInit().toStringWithoutComments() : null)
                                    .build());
                        }
                    } else if (bodyDeclaration instanceof MethodDeclaration) {
                        MethodDeclaration methodDeclaration = (MethodDeclaration) bodyDeclaration;
                        List arguments = new ArrayList();
                        List exceptions = new ArrayList();
                        List methodAnnotations = new ArrayList();
                        for (AnnotationExpr annotationExpr : methodDeclaration.getAnnotations()) {
                            methodAnnotations.add(ANNOTATIONREF.apply(annotationExpr));
                        }
                        for (ReferenceType referenceType : methodDeclaration.getThrows()) {
                            TypeRef exceptionRef = TYPEREF.apply(referenceType.getType());
                            if (exceptionRef instanceof ClassRef) {
                                exceptions.add((ClassRef) exceptionRef);
                            }
                        }
                        Boolean preferVarArg = false;

                        for (Parameter parameter : methodDeclaration.getParameters()) {
                            List paramAnnotations = new ArrayList();
                            for (AnnotationExpr annotationExpr : parameter.getAnnotations()) {
                                paramAnnotations.add(ANNOTATIONREF.apply(annotationExpr));
                            }


                            TypeRef typeRef = TYPEREF.apply(parameter.getType());

                            if (parameter.isVarArgs()) {
                                preferVarArg = true;
                                typeRef = typeRef.withDimensions(typeRef.getDimensions() + 1);
                            }

                            arguments.add(new PropertyBuilder()
                                    .withName(parameter.getId().getName())
                                    .withTypeRef(typeRef)
                                    .withModifiers(parameter.getModifiers())
                                    .withAnnotations(paramAnnotations)
                                    .build());
                        }


                        List typeParamDefs = new ArrayList();
                        for (TypeParameter typeParameter : methodDeclaration.getTypeParameters()) {
                            typeParamDefs.add(TYPEPARAMDEF.apply(typeParameter));
                        }

                        TypeRef returnType = checkAgainstTypeParamRef(TYPEREF.apply(methodDeclaration.getType()), parameters);
                        methods.add(new MethodBuilder()

                                .withName(methodDeclaration.getName())
                                .withModifiers(methodDeclaration.getModifiers())
                                .withParameters(typeParamDefs)
                                .withVarArgPreferred(preferVarArg)
                                .withReturnType(returnType)
                                .withExceptions(exceptions)
                                .withArguments(arguments)
                                .withAnnotations(methodAnnotations)
                                .withBlock(BLOCK.apply(methodDeclaration.getBody()))
                                .build());

                    } else if (bodyDeclaration instanceof ConstructorDeclaration) {
                        ConstructorDeclaration constructorDeclaration = (ConstructorDeclaration) bodyDeclaration;
                        List arguments = new ArrayList();
                        List exceptions = new ArrayList();
                        List ctorAnnotations = new ArrayList();

                        for (AnnotationExpr annotationExpr : constructorDeclaration.getAnnotations()) {
                            ctorAnnotations.add(ANNOTATIONREF.apply(annotationExpr));
                        }
                        for (NameExpr nameExpr : constructorDeclaration.getThrows()) {
                            String name = nameExpr.getName();
                            String packageName = PACKAGENAME.apply(nameExpr);
                            exceptions.add(new ClassRefBuilder().withNewDefinition()
                                    .withName(name)
                                    .withPackageName(packageName)
                            .endDefinition().build());
                        }
                        for (Parameter parameter : constructorDeclaration.getParameters()) {
                            List ctorParamAnnotations = new ArrayList();
                            for (AnnotationExpr annotationExpr : parameter.getAnnotations()) {
                                ctorParamAnnotations.add(ANNOTATIONREF.apply(annotationExpr));
                            }
                            TypeRef typeRef = checkAgainstTypeParamRef(TYPEREF.apply(parameter.getType()), parameters);
                            arguments.add(new PropertyBuilder()
                                    .withName(parameter.getId().getName())
                                    .withTypeRef(typeRef)
                                    .withModifiers(parameter.getModifiers())
                                    .withAnnotations(ctorParamAnnotations)
                                    .build());
                        }
                        constructors.add(new MethodBuilder()
                                .withModifiers(constructorDeclaration.getModifiers())
                                .withExceptions(exceptions)
                                .withArguments(arguments)
                                .withAnnotations(ctorAnnotations)
                                .withBlock(BLOCK.apply(constructorDeclaration.getBlock()))
                                .build());
                    }
                }

               return DefinitionRepository.getRepository().register( new TypeDefBuilder()
                        .withKind(kind)
                        .withPackageName(PACKAGENAME.apply(type))
                        .withName(decl.getName())
                        .withModifiers(type.getModifiers())
                        .withParameters(parameters)
                        .withExtendsList(extendsList)
                        .withImplementsList(implementsList)
                        .withProperties(properties)
                        .withMethods(methods)
                        .withConstructors(constructors)
                        .withAnnotations(annotations)
                        .addToAttributes(TypeDef.ALSO_IMPORT, IMPORTS.apply(type))
                        .build());
            }

            if (type instanceof AnnotationDeclaration) {
                AnnotationDeclaration decl = (AnnotationDeclaration) type;
                Kind kind = Kind.ANNOTATION;
                List methods = new ArrayList();

                for (BodyDeclaration bodyDeclaration : decl.getMembers()) {
                    if (bodyDeclaration instanceof AnnotationMemberDeclaration) {
                        Map attributes = new HashMap<>();
                        AnnotationMemberDeclaration annotationMemberDeclaration = (AnnotationMemberDeclaration) bodyDeclaration;
                        if (annotationMemberDeclaration.getDefaultValue() != null) {
                            attributes.put(Attributeable.DEFAULT_VALUE, annotationMemberDeclaration.getDefaultValue().toString());
                        }
                        TypeRef returnType = TYPEREF.apply(annotationMemberDeclaration.getType());
                        methods.add(new MethodBuilder()
                                .withName(annotationMemberDeclaration.getName())
                                .withModifiers(annotationMemberDeclaration.getModifiers())
                                .withReturnType(returnType)
                                .withAttributes(attributes)
                                .build());
                    }
                }

                List annotations = new ArrayList();
                for (AnnotationExpr annotationExpr : decl.getAnnotations()) {
                    annotations.add(ANNOTATIONREF.apply(annotationExpr));
                }

                return DefinitionRepository.getRepository().register( new TypeDefBuilder()
                        .withKind(kind)
                        .withPackageName(PACKAGENAME.apply(type))
                        .withName(decl.getName())
                        .withModifiers(type.getModifiers())
                        .withMethods(methods)
                        .withAnnotations(annotations)
                        .addToAttributes(TypeDef.ALSO_IMPORT, IMPORTS.apply(type))
                        .build());
            }
            throw new IllegalArgumentException("Unsupported TypeDeclaration:[" + type + "].");
        }

        //To be more accurate we need to check if there is a matching type parameter definition
        //and if so, return a reference to that (rather than consider it a class).
        private TypeRef checkAgainstTypeParamRef(TypeRef typeRef, Collection parameters) {
            TypeParamDef parameterDef = TypeUtils.getParameterDefinition(typeRef, parameters);
            if (parameterDef != null) {
                return parameterDef.toReference();
            }
            return typeRef;
        }
    };


    public static Function FROM_FILE_TO_COMPILATIONUNIT = new Function() {

        public CompilationUnit apply(File file) {
            FileInputStream fis = null;
            try {
                fis = new FileInputStream(file);
                return JavaParser.parse(fis);
            } catch (Exception ex) {
                throw new RuntimeException("Failed to load file: [" + file.getAbsolutePath() + "] from file system.");
            } finally {
                IOUtils.closeQuietly(fis);
            }
        }
    };

    public static Function FROM_CLASSPATH_TO_COMPILATIONUNIT = new Function() {

        public CompilationUnit apply(String resource) {
            InputStream is = null;
            try {
                is = getClass().getClassLoader().getResourceAsStream(resource);
                return JavaParser.parse(is);
            } catch (Exception ex) {
                throw new RuntimeException("Failed to load resource: [" + resource + "] from classpath.");
            } finally {
                IOUtils.closeQuietly(is);
            }
        }
    };

    public static Function FROM_INPUTSTREAM_TO_COMPILATIONUNIT = new Function() {
        public CompilationUnit apply(InputStream is) {
            try {
                return JavaParser.parse(is);
            } catch (Exception ex) {
                throw new RuntimeException("Failed to parse stream.", ex);
            } finally {
                IOUtils.closeQuietly(is);
            }
        }
    };

    public static Function FROM_CLASSPATH_TO_SINGLE_TYPEDEF = new Function() {

        public TypeDef apply(String resource) {
            CompilationUnit cu = Sources.FROM_CLASSPATH_TO_COMPILATIONUNIT.apply(resource);
            TypeDeclaration typeDeclaration = cu.getTypes().get(0);
            return TYPEDEF.apply(typeDeclaration);

        }
    };

    public static Function FROM_INPUTSTEAM_TO_SINGLE_TYPEDEF = new Function() {

        public TypeDef apply(InputStream is) {
            CompilationUnit cu = Sources.FROM_INPUTSTREAM_TO_COMPILATIONUNIT.apply(is);
            TypeDeclaration typeDeclaration = cu.getTypes().get(0);
            return TYPEDEF.apply(typeDeclaration);

        }
    };

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy