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

org.nuiton.jaxx.compiler.tasks.GenerateConstructorsTask Maven / Gradle / Ivy

The newest version!
/*
 * #%L
 * JAXX :: Compiler
 * %%
 * Copyright (C) 2008 - 2024 Code Lutin, Ultreia.io
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Lesser Public License for more details.
 *
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * .
 * #L%
 */
package org.nuiton.jaxx.compiler.tasks;

import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import org.nuiton.jaxx.compiler.CompiledObject;
import org.nuiton.jaxx.compiler.CompilerException;
import org.nuiton.jaxx.compiler.JAXXCompiler;
import org.nuiton.jaxx.compiler.JAXXCompilerFile;
import org.nuiton.jaxx.compiler.JAXXEngine;
import org.nuiton.jaxx.compiler.finalizers.DefaultFinalizer;
import org.nuiton.jaxx.compiler.java.JavaArgument;
import org.nuiton.jaxx.compiler.java.JavaConstructor;
import org.nuiton.jaxx.compiler.java.JavaElementFactory;
import org.nuiton.jaxx.compiler.java.JavaFile;
import org.nuiton.jaxx.compiler.reflect.ClassDescriptor;
import org.nuiton.jaxx.compiler.reflect.ClassDescriptorHelper;
import org.nuiton.jaxx.compiler.reflect.MethodDescriptor;
import org.nuiton.jaxx.compiler.tags.TagManager;
import org.nuiton.jaxx.runtime.JAXXContext;
import org.nuiton.jaxx.runtime.JAXXObject;
import org.nuiton.jaxx.runtime.JAXXUtil;

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

import static java.lang.reflect.Modifier.PUBLIC;

/**
 * Task to execute just after finalize task to create all constructors for any
 * compiler.
 *
 * In fact, we can not compute constructor in one time since some compiler may
 * need of the constructors of previous compiler...
 *
 * This task will compute all constructors to generate.
 *
 * @author Tony Chemit - [email protected]
 * @see JavaConstructor
 * @since 2.4
 */
public class GenerateConstructorsTask extends JAXXEngineTask {

    /** Logger */
    private static final Logger log = LogManager.getLogger(GenerateConstructorsTask.class);

    /** Task name */
    public static final String TASK_NAME = "PostFinalize";

    private static final String PARAMETER_NAME_PARENT_CONTEXT = "parentContext";

    public GenerateConstructorsTask() {
        super(TASK_NAME);
    }

    @Override
    public boolean perform(JAXXEngine engine) throws Exception {
        boolean success = true;
        boolean isVerbose = engine.getConfiguration().isVerbose();

        JAXXCompilerFile[] files = engine.getCompiledFiles();

        // to contains all compilers
        List compilers = new ArrayList<>();
        for (JAXXCompilerFile jaxxFile : files) {
            compilers.add(jaxxFile.getCompiler());
        }

        int round = 0;

        while (!compilers.isEmpty()) {

            if (isVerbose) {
                log.info("Round " + round++ + ", still " +
                                 compilers.size() + " compilers to treat.");
            }

            // launch a round since there is still some compiler to treat
            Iterator itr = compilers.iterator();
            while (itr.hasNext()) {
                JAXXCompiler compiler = itr.next();
                JavaFile javaFile = compiler.getJavaFile();

                boolean isJAXXObject = javaFile.isSuperclassIsJAXXObject();
                if (!isJAXXObject) {

                    // can directly compute constructors

                    if (log.isDebugEnabled()) {
                        log.debug("Compute constructor from non super " +
                                          "jaxx object file " + javaFile.getName());
                    }

                    // get the constructors of the parent class

                    addConstructorsForNoneSuperClassJaxx(engine, compiler);
                    itr.remove();
                    continue;
                }

                // compiler inheritate from a jaxx object
                CompiledObject rootObject = compiler.getRootObject();
                ClassDescriptor parentClassDescriptor = rootObject.getObjectClass();

                if (parentClassDescriptor.getResolverType()
                        != ClassDescriptorHelper.ResolverType.JAXX_FILE) {

                    // the parent was not generated by this engine; we can safely
                    // use it

                    if (log.isDebugEnabled()) {
                        log.debug("Compute constructor from outside super " +
                                          "jaxx object file " + javaFile.getName());
                    }
                    addConstructorsForSuperClassJaxx(engine, compiler, null);
                    itr.remove();
                    continue;

                }

                JAXXCompiler parentCompiler = engine.getJAXXCompiler(
                        JAXXCompiler.getCanonicalName(parentClassDescriptor));


                if (!compilers.contains(parentCompiler)) {

                    // parent was generated by this engine and was laready
                    // treated, can now safely deal this the given compiler

                    if (log.isDebugEnabled()) {
                        log.debug("Compute constructor from inside super " +
                                          "jaxx object file " + javaFile.getName());
                    }

                    addConstructorsForSuperClassJaxx(engine, compiler, parentCompiler);
                    itr.remove();
                    continue;
                }

                // can not treate at the moment...
                if (log.isDebugEnabled()) {
                    log.debug("Can not compute constructors for " +
                                      compiler.getRootObject().getId() +
                                      " waits fro his parent to be treated...");
                }
            }
        }
        return success;
    }

    /**
     * To add constructor on the given {@code compiler}, knowing that the super
     * class of it is not a jaxx class.
     *
     * In this mode, we takes all the constructors of the parent (if parent has
     * some!) and for each of them add the simple one and another one with
     * first parameter a {@link JAXXContext}.
     *
     * @param engine   the current engine which compiled compiler
     * @param compiler the current compiler to treat
     * @throws ClassNotFoundException if a class could not be found (when wanted to have extact type for constructor parameters)
     * @throws IllegalStateException  if given {@code compiler has a super JAXX class}.
     */
    protected void addConstructorsForNoneSuperClassJaxx(JAXXEngine engine,
                                                        JAXXCompiler compiler) throws ClassNotFoundException, IllegalStateException {

        JavaFile javaFile = compiler.getJavaFile();

        if (javaFile.isSuperclassIsJAXXObject()) {
            throw new IllegalStateException(
                    "This method does not accept compiler that " +
                            "inheritates from a jaxx file.");
        }

        String className = javaFile.getSimpleName();

        if (engine.isVerbose()) {
            log.info("start " + javaFile.getName());
        }

        addStartProfileTime(engine, compiler);

        // get already registred constructors : need to keep the list of parameters
        // not to generate a constructor with same prototype twice.
        List> prototypes = getDeclaredConstructorPrototypes(compiler, javaFile);

        MethodDescriptor[] constructorDescriptors =
                compiler.getRootObject().getObjectClass().getConstructorDescriptors();

        List constructorTypes;
        boolean canAddConstructor;

        if (constructorDescriptors == null || constructorDescriptors.length == 0) {

            // no constructors (use only a default constructor)

            constructorTypes = getConstructorTypes();
            canAddConstructor = canAddConstructor(prototypes, constructorTypes);
            if (canAddConstructor) {
                addConstructor(compiler, className, constructorTypes);
            }

            constructorTypes.add(0, JAXXCompiler.getCanonicalName(JAXXContext.class));
            canAddConstructor = canAddConstructor(prototypes, constructorTypes);
            if (canAddConstructor) {
                addConstructorWithInitialContext(compiler, className, constructorTypes, false);
            }
        } else {
            for (MethodDescriptor constructorDescriptor : constructorDescriptors) {

                constructorTypes = getConstructorTypes(constructorDescriptor.getParameterTypes());
                canAddConstructor = canAddConstructor(prototypes, constructorTypes);
                if (canAddConstructor) {
                    addConstructor(compiler, className, constructorTypes);
                }

                constructorTypes.add(0, JAXXCompiler.getCanonicalName(JAXXContext.class));
                canAddConstructor = canAddConstructor(prototypes, constructorTypes);
                if (canAddConstructor) {
                    addConstructorWithInitialContext(compiler, className, constructorTypes, false);
                }
            }
        }

        addEndProfileTime(engine, compiler);
    }

    /**
     * To add constructor on the given {@code compiler}, knowing that the super
     * class of it is a jaxx class.
     *
     * In this mode, we takes all the constructors of the parent (if parent has
     * some!) and for each of them add the simple one and another one with
     * first parameter a {@link JAXXContext}.
     *
     * @param engine         the current engine which compiled compiler
     * @param compiler       the current compiler to treat
     * @param parentCompiler the compiler of the super class (can be
     *                       {@code null} if super class was not generated by
     *                       the given engine).
     * @throws ClassNotFoundException if a class could not be found (when wanted to have extact type for constructor parameters)
     * @throws IllegalStateException  if given {@code compiler has not a super JAXX class}.
     */
    protected void addConstructorsForSuperClassJaxx(JAXXEngine engine,
                                                    JAXXCompiler compiler,
                                                    JAXXCompiler parentCompiler) throws ClassNotFoundException, IllegalStateException {

        JavaFile javaFile = compiler.getJavaFile();

        if (!javaFile.isSuperclassIsJAXXObject()) {
            throw new IllegalStateException(
                    "This method does not accept compiler that " +
                            "inheritates not from a jaxx file.");
        }

        String className = javaFile.getSimpleName();

        if (engine.isVerbose()) {
            log.info("start " + javaFile.getName());
        }

        addStartProfileTime(engine, compiler);

        // get already registred constructors : need to keep the list of parameters
        // not to generate a constructor with same prototype twice.
        List> prototypes = getDeclaredConstructorPrototypes(compiler, javaFile);

        MethodDescriptor[] constructorDescriptors;

        if (parentCompiler == null) {

            // the parent was not generated by this engine, this means that is
            // class descriptor can be used to obtain constructors
            constructorDescriptors = compiler.getRootObject().getObjectClass().getConstructorDescriptors();
        } else {

            // the parent was generated by this engine, can not trust the class
            // descriptor at the moment, so just seek in his java file for
            // already generated constructor
            List constructors = parentCompiler.getJavaFile().getConstructors();
            constructorDescriptors = new MethodDescriptor[constructors.size()];

            int i = 0;
            for (JavaConstructor constructor : constructors) {
                String[] parameters = new String[constructor.getArguments().length];
                int j = 0;
                for (JavaArgument argument : constructor.getArguments()) {
                    String type = argument.getType();
                    parameters[j++] = type;
                }
                constructorDescriptors[i++] = new MethodDescriptor(
                        null,
                        constructor.getModifiers(),
                        null,
                        parameters,
                        compiler.getClassLoader()
                );
            }
        }

        // dealing with a jsuper class JAXX we are sure to have at least two constructors :
        // a default one + one with just a JAXXContext parameter

        List constructorTypes;
        boolean canAddConstructor;

        for (MethodDescriptor constructorDescriptor : constructorDescriptors) {

            ClassDescriptor[] parameterTypes = constructorDescriptor.getParameterTypes();

            if (parentCompiler == null) {

                // we already have the good type ??? this is dangerous
                // because we could miss an import ? must be improved
                constructorTypes = new ArrayList<>(parameterTypes.length);

                for (ClassDescriptor parameterType : parameterTypes) {
                    constructorTypes.add(parameterType.getName());
                }
            } else {


                constructorTypes = getConstructorTypes(parameterTypes);
            }
            canAddConstructor = canAddConstructor(prototypes, constructorTypes);
            if (canAddConstructor) {
                addConstructor(compiler, className, constructorTypes);
            }

//            constructorTypes.add(0, JAXXCompiler.getCanonicalName(JAXXContext.class));
//            canAddConstructor = canAddConstructor(prototypes, constructorTypes);
//            if (canAddConstructor) {
//                addConstructorWithInitialContext(compiler, className, constructorTypes, true);
//            }
        }


        addEndProfileTime(engine, compiler);
    }

    protected List> getDeclaredConstructorPrototypes(JAXXCompiler compiler,
                                                                  JavaFile javaFile) throws ClassNotFoundException {
        List constructors = javaFile.getConstructors();
        List> prototypes = new ArrayList<>(constructors.size());
        for (JavaConstructor constructor : constructors) {
            List prototype = new ArrayList<>();
            for (JavaArgument argument : constructor.getArguments()) {
                String type = argument.getType();
                String fqn = TagManager.resolveClassName(type, compiler);
                ClassDescriptor classDescriptor = ClassDescriptorHelper.getClassDescriptor(fqn);
                String canonicalName = JAXXCompiler.getCanonicalName(classDescriptor);
                prototype.add(canonicalName);
            }
            prototypes.add(prototype);
        }
        return prototypes;
    }

    private boolean canAddConstructor(List> prototypes, List constructorTypes) {
        return !prototypes.contains(constructorTypes);
    }

    private List getConstructorTypes(ClassDescriptor... descriptors) {
        List result = new ArrayList<>();
        // add all parameters from constructor
        for (ClassDescriptor descriptor : descriptors) {
            String fqn = JAXXCompiler.getCanonicalName(descriptor);
            result.add(fqn);
        }
        return result;
    }

    protected void addConstructor(JAXXCompiler compiler,
                                  String className,
                                  List constructorTypes) throws CompilerException {
        StringBuilder code = new StringBuilder();
        String eol = JAXXCompiler.getLineSeparator();

        JavaArgument[] arguments = new JavaArgument[constructorTypes.size()];

        if (!constructorTypes.isEmpty()) {

            // constructeur avec des paramètres
            code.append("        super(");
            int i = 0;
            for (String constructorType : constructorTypes) {
                JavaArgument argument = JavaElementFactory.newArgument(
                        constructorType,
                        "param" + i
                );
                arguments[i] = argument;
                if (i > 0) {
                    code.append(" ,");
                }
                code.append(argument.getName());
                i++;
            }

            code.append(");").append(eol);
        }
        if (!compiler.isSuperClassAware(JAXXObject.class)) {
            code.append(DefaultFinalizer.METHOD_NAME_$INITIALIZE + "();");
        }
        code.append(eol);
        JavaConstructor constructor = JavaElementFactory.newConstructor(PUBLIC,
                                                                        className,
                                                                        code.toString(),
                                                                        arguments
        );
        compiler.getJavaFile().addConstructor(constructor);
    }

    protected void addConstructorWithInitialContext(JAXXCompiler compiler,
                                                    String className,
                                                    List constructorTypes,
                                                    boolean superclassIsJAXXObject) throws CompilerException {
        StringBuilder code = new StringBuilder();
        String eol = JAXXCompiler.getLineSeparator();

        JavaArgument firstArgument = JavaElementFactory.newArgument(
                JAXXContext.class.getName(),
                PARAMETER_NAME_PARENT_CONTEXT
        );
        JavaArgument[] arguments = new JavaArgument[constructorTypes.size()];
        arguments[0] = firstArgument;
        for (int i = 1, max = constructorTypes.size(); i < max; i++) {
            String constructorType = constructorTypes.get(i);
            JavaArgument argument = JavaElementFactory.newArgument(
                    constructorType,
                    "param" + i
            );
            arguments[i] = argument;
        }
        if (superclassIsJAXXObject) {

            // we are sure to have at least the first parameter in the super code
            code.append("        super(");
            code.append(PARAMETER_NAME_PARENT_CONTEXT);
            for (int i = 1, max = constructorTypes.size(); i < max; i++) {
                String constructorType = constructorTypes.get(i);
                JavaArgument argument = JavaElementFactory.newArgument(
                        constructorType,
                        "param" + i
                );
                arguments[i] = argument;
                code.append(" ,");
                code.append(argument.getName());
            }
            code.append(");").append(eol);
        } else {

            // only a super class only if more than the parentContext parameter
            if (constructorTypes.size() > 1) {

                code.append("        super(");

                for (int i = 1, max = constructorTypes.size(); i < max; i++) {
                    String constructorType = constructorTypes.get(i);
                    JavaArgument argument = JavaElementFactory.newArgument(
                            constructorType,
                            "param" + i
                    );
                    arguments[i] = argument;
                    if (i > 1) {
                        code.append(" ,");
                    }
                    code.append(argument.getName());
                }
                code.append(");").append(eol);
            }
        }

        if (!superclassIsJAXXObject) {

            // call explicitly the init code of the parentContext
            String prefix = compiler.getImportedType(JAXXUtil.class);
            code.append(prefix);
            code.append(".initContext(this, " + PARAMETER_NAME_PARENT_CONTEXT + ");");
            code.append(eol);
        }
        code.append(DefaultFinalizer.METHOD_NAME_$INITIALIZE + "();");
        code.append(eol);
        JavaConstructor constructor = JavaElementFactory.newConstructor(PUBLIC,
                                                                        className,
                                                                        code.toString(),
                                                                        arguments
        );
        compiler.getJavaFile().addConstructor(constructor);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy