org.nuiton.jaxx.compiler.JAXXEngine Maven / Gradle / Ivy
/*
* #%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;
import io.ultreia.java4all.i18n.spi.builder.I18nKeySet;
import io.ultreia.java4all.i18n.spi.builder.I18nModuleNotInitializedException;
import io.ultreia.java4all.lang.Strings;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.nuiton.jaxx.compiler.finalizers.I18nKeysFileModel;
import org.nuiton.jaxx.compiler.reflect.ClassDescriptor;
import org.nuiton.jaxx.compiler.reflect.ClassDescriptorHelper;
import org.nuiton.jaxx.compiler.reflect.FieldDescriptor;
import org.nuiton.jaxx.compiler.reflect.MethodDescriptor;
import org.nuiton.jaxx.compiler.tasks.CompileFirstPassTask;
import org.nuiton.jaxx.compiler.tasks.CompileSecondPassTask;
import org.nuiton.jaxx.compiler.tasks.FinalizeTask;
import org.nuiton.jaxx.compiler.tasks.GenerateConstructorsTask;
import org.nuiton.jaxx.compiler.tasks.GenerateMissingRulesTask;
import org.nuiton.jaxx.compiler.tasks.GenerateTask;
import org.nuiton.jaxx.compiler.tasks.InitTask;
import org.nuiton.jaxx.compiler.tasks.JAXXEngineTask;
import org.nuiton.jaxx.compiler.tasks.ProfileTask;
import org.nuiton.jaxx.compiler.tasks.StyleSheetTask;
import java.beans.Introspector;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.TreeMap;
/**
* The engine to compile jaxx files.
*
* The method {@link #run()} launch the compilation of files.
*
* @author Tony Chemit - [email protected]
* @since 2.0.0 was previously JAXXCompilerLaunchor
*/
@SuppressWarnings("unused")
public class JAXXEngine {
/** Logger */
private static final Logger log = LogManager.getLogger(JAXXEngine.class);
/** configuration of the launchor and underlines compilers */
protected final CompilerConfiguration configuration;
/** original list of files to compile (says the detected modfied files) */
protected final JAXXCompilerFile[] incomingFiles;
/** Files to compile */
protected final List compilingFiles;
/** Warnings detected while running. */
protected final List warnings;
/** Errors detected while running. */
protected final List errors;
/** To manage i18n keys. */
private final I18nKeySet getterFile;
/**
*
*/
private final Map i18nPropertyMapping;
private final Map i18nKeys = new TreeMap<>();
/** tasks to launch */
protected JAXXEngineTask[] tasks;
/** current pass of the engine */
protected JAXXEngineTask currentTask;
/** profile attached to the engine (can be null) */
protected JAXXProfile profiler;
private Class> validatorFactory;
private ClassDescriptor validatorFactoryClass;
protected JAXXEngine(CompilerConfiguration configuration,
File base,
String... relativePaths) {
if (configuration == null) {
// use a default configuration
configuration = new DefaultCompilerConfiguration();
}
this.configuration = configuration;
warnings = new ArrayList<>();
errors = new ArrayList<>();
compilingFiles = new ArrayList<>();
i18nPropertyMapping = new TreeMap<>();
if (configuration.isShowClassDescriptorLoading()) {
ClassDescriptorHelper.setShowLoading(true);
}
// add all default files to compile
for (String relativePath : relativePaths) {
JAXXCompilerFile compilerFile =
new JAXXCompilerFile(base, new File(base, relativePath), configuration.getCssExtension());
addFileToCompile(compilerFile);
}
if (configuration.getI18nModule() != null) {
try {
this.getterFile = configuration.getI18nModule().getModuleKeySet("jaxx");
} catch (I18nModuleNotInitializedException e) {
throw new RuntimeException("No i18n configuration found, please invoke mojo i18n:init");
}
} else {
this.getterFile = null;
}
// fix once for all incoming files to compile
incomingFiles = compilingFiles.toArray(new JAXXCompilerFile[0]);
JAXXCompiler.commonCssDetectedCount = 0;
}
public JAXXEngineTask[] getTasks() {
if (tasks == null) {
List tasks = new ArrayList<>();
tasks.add(new InitTask());
tasks.add(new CompileFirstPassTask());
tasks.add(new CompileSecondPassTask());
tasks.add(new StyleSheetTask());
tasks.add(new FinalizeTask());
tasks.add(new GenerateConstructorsTask());
tasks.add(new GenerateTask());
if (getConfiguration().isGenerateMissingIdsAndStyleClassesInCss()) {
tasks.add(new GenerateMissingRulesTask());
}
if (getConfiguration().isProfile()) {
tasks.add(new ProfileTask());
}
this.tasks = tasks.toArray(new JAXXEngineTask[0]);
}
return tasks;
}
/** @return the errors of the engine */
public List getErrors() {
return errors;
}
/** @return the warnings of the engine */
public List getWarnings() {
return warnings;
}
public JAXXProfile getProfiler() {
if (profiler == null && getConfiguration().isProfile()) {
profiler = new JAXXProfile();
}
return profiler;
}
public CompilerConfiguration getConfiguration() {
return configuration;
}
public JAXXCompilerFile[] getFilesToCompile() {
List files = new ArrayList<>();
for (JAXXCompilerFile file : compilingFiles) {
if (file.getCompiler() == null) {
files.add(file);
}
}
return files.toArray(new JAXXCompilerFile[0]);
}
public JAXXCompilerFile[] getCompiledFiles() {
List files = new ArrayList<>();
for (JAXXCompilerFile file : compilingFiles) {
if (file.getCompiler() != null) {
files.add(file);
}
}
// always send a copy to be safe.
return files.toArray(new JAXXCompilerFile[0]);
}
public boolean containsJaxxFileClassName(String className) {
for (JAXXCompilerFile file : compilingFiles) {
if (className.equals(file.getClassName())) {
return true;
}
}
return false;
}
public boolean isCompileFirstPassTask() {
return currentTask != null &&
CompileFirstPassTask.TASK_NAME.equals(currentTask.getName());
}
/**
* Resets all state in preparation for a new compilation session.
*
* @param cleanReports should we also clean reports ?
*/
protected void reset(boolean cleanReports) {
for (JAXXCompilerFile compilerFile : compilingFiles) {
compilerFile.clear();
}
compilingFiles.clear();
if (profiler != null) {
profiler.clear();
profiler = null;
}
if (cleanReports) {
clearReports();
}
ClassDescriptorHelper.setShowLoading(false);
}
public void clearReports() {
getWarnings().clear();
getErrors().clear();
}
public String getVersion() {
return "2.4";
}
/**
* Obtain the jaxx compiler of the given class name.
*
* @param className the name of the class to use
* @return the compiler instance which is processing the specified JAXX class.
* Each class is compiled by a different compiler instance.
*/
public JAXXCompilerFile getJAXXCompilerFile(String className) {
for (JAXXCompilerFile compilingFile : compilingFiles) {
if (className.equals(compilingFile.getClassName())) {
return compilingFile;
}
}
return null;
}
/**
* Obtain the jaxx compiler of the given class name.
*
* @param className the name of the class to use
* @return the compiler instance which is processing the specified JAXX class.
* Each class is compiled by a different compiler instance.
*/
public JAXXCompiler getJAXXCompiler(String className) {
JAXXCompilerFile compilerFile = getJAXXCompilerFile(className);
if (compilerFile == null) {
return null;
}
return compilerFile.getCompiler();
}
/**
* Obtain the decorator of the given name.
*
* @param name the name of the decorator
* @return the decorator found.
* @throws IllegalArgumentException if decorator not found for the given name.
*/
public CompiledObjectDecorator getDecorator(String name)
throws IllegalArgumentException {
Map decorators =
getConfiguration().getDecorators();
CompiledObjectDecorator decorator = decorators.get(name);
if (decorator == null) {
throw new IllegalArgumentException(
"could not find decorator with key " + name +
" (known decorators : " + decorators.keySet() + ")");
}
return decorator;
}
/**
* Obtain the decorator of the given type.
*
* @param type the type of the decorator (syas his fqn)
* @return the decorator found
*/
public CompiledObjectDecorator getDecorator(Class> type) {
Map decorators =
getConfiguration().getDecorators();
for (CompiledObjectDecorator decorator : decorators.values()) {
if (decorator.getClass().equals(type)) {
return decorator;
}
}
return null;
}
/**
* Add a warning to the engine.
*
* @param warning the warning to add
*/
public void addWarning(String warning) {
warnings.add(warning);
}
/**
* Add an error to the engine.
*
* @param error the error to add
*/
public void addError(String error) {
errors.add(error);
}
/**
* Compiled a set of files.
*
* @return {@code -1} if errors appears, the number of generated files
* otherwise.
*/
public int run() {
try {
boolean success = true;
for (JAXXEngineTask task : getTasks()) {
if (!success) {
// stop as soon as a engine phase failed
break;
}
currentTask = task;
long t0 = System.nanoTime();
if (isVerbose()) {
log.info("Start task '" + task.getName() + "' on " +
compilingFiles.size() + " file(s)");
}
success = task.perform(this);
if (isVerbose()) {
log.info("task '" + task.getName() + "' done in " +
Strings.convertTime(System.nanoTime() - t0)
);
}
}
return success ? compilingFiles.size() : -1;
//FIXME : deal better the exception treatment...
} catch (Throwable e) {
log.error(e.getMessage(), e);
return -1;
} finally {
if (configuration.isResetAfterCompile()) {
reset(false);
}
}
}
/**
* Adds a {@code file} to be compiled.
*
* @param file the {@link JAXXCompilerFile} to add.
*/
public void addFileToCompile(JAXXCompilerFile file) {
if (isVerbose()) {
log.info("register jaxx file " + file.getJaxxFile());
}
compilingFiles.add(file);
}
/**
* Adds a {@link JAXXCompilerFile} given the jaxx file and the
* corresponding class fully qualified name.
*
* @param jaxxFile the jaxx file location
* @param jaxxClassName the fully qualified name of the jaxx class
*/
public void addFileToCompile(File jaxxFile, String jaxxClassName) {
if (log.isDebugEnabled()) {
log.debug("file = " + jaxxFile + ", fqn = " + jaxxClassName);
}
JAXXCompilerFile file = new JAXXCompilerFile(jaxxFile, jaxxClassName, configuration.getCssExtension());
addFileToCompile(file);
}
/**
* Create a new compiler and attach it to the given {@code jaxxFile}.
*
* @param jaxxFile the definition of jaxx file to compile
* @return the new compiler
* @throws Exception if any pb while creating of compiler
*/
public JAXXCompiler newCompiler(JAXXCompilerFile jaxxFile) throws Exception {
Class> compilerClass =
getConfiguration().getCompilerClass();
if (compilerClass == null) {
throw new NullPointerException(
"Configuration compilerClass is null");
}
Constructor> cons = compilerClass.getConstructor(
JAXXEngine.class,
JAXXCompilerFile.class,
List.class
);
/*
Arrays.asList(
"java.awt.*",
"java.awt.event.*",
"java.io.*",
"java.lang.*",
"java.util.*",
"javax.swing.*",
"javax.swing.border.*",
"javax.swing.event.*",
"jaxx.runtime.*",
"jaxx.runtime.swing.*",
"static io.ultreia.java4all.i18n.I18n.t",
"static org.nuiton.jaxx.runtime.swing.SwingUtil.createImageIcon"
)
*/
JAXXCompiler jaxxCompiler = (JAXXCompiler) cons.newInstance(
this,
jaxxFile,
Arrays.asList(
"java.awt.*",
// "java.awt.event.*",
// "java.io.*",
"java.lang.*",
// "java.util.*",
"javax.swing.*",
"javax.swing.border.*",
// "javax.swing.event.*",
// "jaxx.runtime.*",
"org.nuiton.jaxx.runtime.swing.*"
)
);
jaxxFile.setCompiler(jaxxCompiler);
return jaxxCompiler;
}
public boolean isVerbose() {
return getConfiguration().isVerbose();
}
/**
* Add a profile time for the given compiler and key.
*
* Note: if {@link #profiler} is {@code null}, do nothing
*
* @param compiler the compiler to profile
* @param key the key of profiling
*/
public void addProfileTime(JAXXCompiler compiler, String key) {
JAXXProfile profiler = getProfiler();
if (profiler != null) {
profiler.addTime(compiler, key);
}
}
String getI18nProperty(ClassDescriptor objectClass) {
String className = objectClass.getName();
String result = i18nPropertyMapping.get(className);
if (result == null) {
Optional field = Optional.empty();
for (String i18nAttribute : I18nHelper.I18N_ATTRIBUTES) {
field = objectClass.tryToGetFieldDescriptor(i18nAttribute);
if (field.isPresent()) {
break;
}
}
if (field.isPresent()) {
result = field.get().getName();
if (isVerbose()) {
log.info(String.format("Discover i18n property: %s for %s", result, className));
}
i18nPropertyMapping.put(className, result);
} else {
Optional method = Optional.empty();
for (String i18nAttribute : I18nHelper.I18N_ATTRIBUTES) {
method = objectClass.tryToGetSetterMethodDescriptor(i18nAttribute);
if (method.isPresent()) {
break;
}
}
if (method.isPresent()) {
result = Introspector.decapitalize(StringUtils.removeStart(method.get().getName(), "set"));
if (isVerbose()) {
log.info(String.format("Discover i18n property: %s for %s", result, className));
}
i18nPropertyMapping.put(className, result);
} else {
throw new IllegalStateException(String.format("Can't find i18n property for type: %s", className));
}
}
}
return result;
}
public void registerI18nKey(String key) {
getterFile.addKey(key);
}
public void flushI18nGetterFile() throws IOException {
if (getterFile != null) {
configuration.getI18nModule().storeModuleKeySet(getterFile);
}
}
public Class> getValidatorFactory() {
try {
return validatorFactory == null ? validatorFactory = Class.forName(getConfiguration().getValidatorFactoryFQN(), false, getConfiguration().getClassLoader()) : validatorFactory;
} catch (ClassNotFoundException e) {
throw new IllegalStateException("Can't find validator class", e);
}
}
public ClassDescriptor getValidatorFactoryClass() {
try {
return validatorFactoryClass == null ? validatorFactoryClass = ClassDescriptorHelper.getClassDescriptor(getConfiguration().getValidatorFactoryFQN(), getConfiguration().getClassLoader()) : validatorFactoryClass;
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e);
}
}
public I18nKeysFileModel getI18nKeysFileModel(String fqn, boolean loadIfNotFound) {
I18nKeysFileModel result = i18nKeys.get(fqn);
if (result == null && loadIfNotFound) {
result = I18nKeysFileModel.of(getConfiguration().getClassLoader(), fqn, getConfiguration().isVerbose());
if (result != null) {
i18nKeys.put(fqn, result);
}
}
return result;
}
}