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);
}
}