org.nuiton.jaxx.compiler.finalizers.DefaultFinalizer 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.finalizers;
import com.google.auto.service.AutoService;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.codehaus.plexus.component.annotations.Component;
import org.nuiton.jaxx.compiler.CompiledObject;
import org.nuiton.jaxx.compiler.CompiledObjectDecorator;
import org.nuiton.jaxx.compiler.CompilerException;
import org.nuiton.jaxx.compiler.EventHandler;
import org.nuiton.jaxx.compiler.JAXXCompiler;
import org.nuiton.jaxx.compiler.binding.DataBinding;
import org.nuiton.jaxx.compiler.binding.writers.DefaultJAXXBindingWriter;
import org.nuiton.jaxx.compiler.binding.writers.JAXXBindingWriter;
import org.nuiton.jaxx.compiler.binding.writers.SimpleJAXXObjectBindingWriter;
import org.nuiton.jaxx.compiler.java.JavaArgument;
import org.nuiton.jaxx.compiler.java.JavaElementFactory;
import org.nuiton.jaxx.compiler.java.JavaField;
import org.nuiton.jaxx.compiler.java.JavaFile;
import org.nuiton.jaxx.compiler.java.JavaFileGenerator;
import org.nuiton.jaxx.compiler.java.JavaMethod;
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.types.TypeManager;
import org.nuiton.jaxx.runtime.Base64Coder;
import org.nuiton.jaxx.runtime.JAXXBinding;
import org.nuiton.jaxx.runtime.JAXXContext;
import org.nuiton.jaxx.runtime.JAXXObject;
import org.nuiton.jaxx.runtime.JAXXObjectDescriptor;
import org.nuiton.jaxx.runtime.JAXXUtil;
import org.nuiton.jaxx.runtime.spi.UIHandler;
import org.nuiton.jaxx.runtime.swing.SwingUtil;
import java.awt.Container;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import static java.lang.reflect.Modifier.ABSTRACT;
import static java.lang.reflect.Modifier.FINAL;
import static java.lang.reflect.Modifier.PRIVATE;
import static java.lang.reflect.Modifier.PROTECTED;
import static java.lang.reflect.Modifier.PUBLIC;
import static java.lang.reflect.Modifier.STATIC;
import static java.lang.reflect.Modifier.isProtected;
import static java.lang.reflect.Modifier.isPublic;
import static org.nuiton.jaxx.compiler.java.JavaElementFactory.newArgument;
import static org.nuiton.jaxx.compiler.java.JavaElementFactory.newField;
import static org.nuiton.jaxx.compiler.java.JavaElementFactory.newMethod;
/**
* This class is a refactoring of the {@link JAXXCompiler}.
*
* We delegate now the generation of a {@link JAXXObject} to this class, the
* {@link JAXXCompiler} now only deals with the compilation of files.
*
* @author Tony Chemit - [email protected]
*/
@AutoService(JAXXCompilerFinalizer.class)
@Component(hint = "default", role = JAXXCompilerFinalizer.class)
public class DefaultFinalizer extends AbstractFinalizer {
public static final String FIELD_NAME_$BINDING_SOURCES = "$bindingSources";
public static final String FIELD_NAME_$OBJECT_MAP = "$objectMap";
public static final String FIELD_NAME_$ACTIVE_BINDINGS = "$activeBindings";
public static final String FIELD_NAME_$PREVIOUS_VALUES = "$previousValues";
public static final String FIELD_NAME_$BINDINGS = "$bindings";
public static final String FIELD_NAME_$PROPERTY_CHANGE_SUPPORT = "$propertyChangeSupport";
public static final String FIELD_NAME_DELEGATE_CONTEXT = "delegateContext";
public static final String FIELD_NAME_SERIAL_VERSION_UID = "serialVersionUID";
public static final String FIELD_NAME_$JAXX_OBJECT_DESCRIPTOR = "$jaxxObjectDescriptor";
public static final String METHOD_NAME_$GET_JAXX_OBJECT_DESCRIPTOR = "$getJAXXObjectDescriptor";
public static final String METHOD_NAME_REGISTER_DATA_BINDING = "registerDataBinding";
public static final String METHOD_NAME_REMOVE_DATA_BINDING = "removeDataBinding";
public static final String METHOD_NAME_APPLY_DATA_BINDING = "applyDataBinding";
public static final String METHOD_NAME_PROCESS_DATA_BINDING = "processDataBinding";
public static final String METHOD_NAME_FIRE_PROPERTY_CHANGE = "firePropertyChange";
public static final String METHOD_NAME_$GET_PROPERTY_CHANGE_SUPPORT = "$getPropertyChangeSupport";
public static final String METHOD_NAME_$INITIALIZE = "$initialize";
public static final String METHOD_NAME_$INITIALIZE_01_CREATE_HANDLER = "$initialize_01_createHandler";
public static final String METHOD_NAME_$INITIALIZE_01_CREATE_COMPONENTS = "$initialize_01_createComponents";
public static final String METHOD_NAME_$INITIALIZE_02_REGISTER_DATA_BINDINGS = "$initialize_02_registerDataBindings";
public static final String METHOD_NAME_$INITIALIZE_03_FINALIZE_CREATE_COMPONENTS = "$initialize_03_finalizeCreateComponents";
public static final String METHOD_NAME_$INITIALIZE_03_REGISTER_ACTIONS = "$initialize_03_registerActions";
public static final String METHOD_NAME_$INITIALIZE_04_APPLY_DATA_BINDINGS = "$initialize_04_applyDataBindings";
public static final String METHOD_NAME_$INITIALIZE_05_SET_PROPERTIES = "$initialize_05_setProperties";
public static final String METHOD_NAME_$INITIALIZE_06_FINALIZE_INITIALIZE = "$initialize_06_finalizeInitialize";
public static final String METHOD_$INITIALIZER_CALL = "" +
"JAXXObject.initialize(%1$s" +
" this,%1$s" +
" this::%2$s,%1$s" +
" this::%3$s,%1$s" +
" this::%4$s,%1$s" +
" this::%5$s,%1$s" +
" this::%6$s,%1$s" +
" this::%7$s,%1$s" +
" this::%8$s,%1$s" +
" this::%9$s);%1$s";
/**
* Logger.
*/
protected static final Logger log = LogManager.getLogger(DefaultFinalizer.class);
/**
* serialVersionUID field
*/
protected static final JavaField SERIAL_VERSION_UID_FIELD = newField(
PRIVATE | STATIC | FINAL,
"long",
FIELD_NAME_SERIAL_VERSION_UID,
false,
"1L"
);
/**
*
*/
protected static final JavaField ACTIVE_BINDINGS_FIELD = newField(
PROTECTED,
List.class.getName() + "<" + TYPE_OBJECT + ">",
FIELD_NAME_$ACTIVE_BINDINGS,
false,
"new %s<" + TYPE_OBJECT + ">()",
ArrayList.class.getName()
);
/**
*
*/
protected static final JavaField BINDING_SOURCES_FIELD = newField(
PROTECTED,
Map.class.getName() + "<" + TYPE_STRING + ", " + TYPE_OBJECT + ">",
FIELD_NAME_$BINDING_SOURCES,
false,
"new %s<" + TYPE_STRING + ", " + TYPE_OBJECT + ">()",
HashMap.class.getName()
);
/**
*
*/
protected static final JavaField OBJECT_MAP_FIELD = newField(
PROTECTED,
Map.class.getName() + "<" + TYPE_STRING + ", " + TYPE_OBJECT + ">",
FIELD_NAME_$OBJECT_MAP,
true,
"new %s<" + TYPE_STRING + ", " + TYPE_OBJECT + ">()",
HashMap.class.getName()
);
/**
*
*/
protected static final JavaField PREVIOUS_VALUES_FIELD = newField(
PROTECTED,
Map.class.getName() + ",?>", FIELD_NAME_$PREVIOUS_VALUES,
false,
"new %s<" + TYPE_OBJECT + ", " + TYPE_OBJECT + ">()",
HashMap.class.getName()
);
/**
*
*/
protected static final JavaField BINDINGS_FIELD = newField(
PROTECTED | FINAL,
Map.class.getName() + "<" + TYPE_STRING + ", " + JAXXBinding.class.getName() + ">",
FIELD_NAME_$BINDINGS,
false,
"new %s<" + TYPE_STRING + ", %s>()",
TreeMap.class.getName(),
JAXXBinding.class.getName()
);
/**
*
*/
protected static final JavaField PROPERTY_CHANGE_SUPPORT_FIELD = newField(
PROTECTED,
PropertyChangeSupport.class.getName(),
FIELD_NAME_$PROPERTY_CHANGE_SUPPORT,
false
);
/**
*
*/
protected static final JavaMethod GET_CONTEXT_VALUE_METHOD = newMethod(
PUBLIC,
" T",
"getContextValue",
"return " + FIELD_NAME_DELEGATE_CONTEXT + ".getContextValue(clazz, null);",
true,
newArgument("Class", "clazz")
);
/**
*
*/
protected static final JavaMethod GET_CONTEXT_VALUE_NAMED_METHOD = newMethod(
PUBLIC,
" T",
"getContextValue",
"return " + FIELD_NAME_DELEGATE_CONTEXT + ".getContextValue(clazz, name);",
true,
newArgument("Class", "clazz"), newArgument(TYPE_STRING, "name")
);
/**
*
*/
protected static final JavaMethod SET_CONTEXT_VALUE_NAMED_METHOD = newMethod(
PUBLIC,
" " + TYPE_VOID,
"setContextValue",
FIELD_NAME_DELEGATE_CONTEXT + ".setContextValue(o, name);",
true,
newArgument("T", "o"), newArgument(TYPE_STRING, "name"));
/**
*
*/
protected static final JavaMethod SET_CONTEXT_VALUE_METHOD = newMethod(
PUBLIC,
" " + TYPE_VOID,
"setContextValue",
FIELD_NAME_DELEGATE_CONTEXT + ".setContextValue(o, null);",
true,
newArgument("T", "o")
);
/**
*
*/
protected static final JavaMethod REMOVE_CONTEXT_VALUE_NAMED_METHOD = newMethod(
PUBLIC,
" " + TYPE_VOID,
"removeContextValue",
FIELD_NAME_DELEGATE_CONTEXT + ".removeContextValue(clazz, name);",
true,
newArgument("Class", "clazz"),
newArgument(TYPE_STRING, "name")
);
/**
*
*/
protected static final JavaMethod REMOVE_CONTEXT_VALUE_METHOD = newMethod(
PUBLIC,
" " + TYPE_VOID,
"removeContextValue",
FIELD_NAME_DELEGATE_CONTEXT + ".removeContextValue(clazz, null);",
true,
newArgument("Class", "clazz")
);
/**
*
*/
protected static final JavaMethod GET_PARENT_CONTAINER_MORE_METHOD = newMethod(
PUBLIC,
" O",
"getParentContainer",
"return %s.getParentContainer(source, clazz);",
true,
newArgument(TYPE_OBJECT, "source"),
newArgument("Class", "clazz")
);
/**
*
*/
protected static final JavaMethod GET_PARENT_CONTAINER_METHOD = newMethod(
PUBLIC,
" O",
"getParentContainer",
"return %s.getParentContainer(this, clazz);",
true,
newArgument("Class", "clazz")
);
/**
*
*/
protected static final JavaMethod GET_OBJECT_BY_ID_METHOD = newMethod(
PUBLIC, TYPE_OBJECT, "getObjectById",
"return " + FIELD_NAME_$OBJECT_MAP + ".get(id);",
true,
newArgument(TYPE_STRING, "id")
);
/**
*
*/
protected static final JavaMethod GET_JAXX_OBJECT_DESCRIPTOR_METHOD = newMethod(
PUBLIC | STATIC,
JAXXObjectDescriptor.class.getName(),
METHOD_NAME_$GET_JAXX_OBJECT_DESCRIPTOR,
"return %s.decodeCompressedJAXXObjectDescriptor(" +
FIELD_NAME_$JAXX_OBJECT_DESCRIPTOR + ");",
false
);
/**
*
*/
protected static final JavaMethod REGISTER_DATA_BINDING_METHOD = newMethod(
PUBLIC,
TYPE_VOID,
METHOD_NAME_REGISTER_DATA_BINDING,
FIELD_NAME_$BINDINGS + ".put(binding.getId(), binding);",
true,
newArgument(JAXXBinding.class.getName(), "binding")
);
/**
*
*/
protected static final JavaMethod GET_DATA_BINDINGS_METHOD = newMethod(
PUBLIC,
JAXXBinding.class.getName() + "[]",
"getDataBindings",
"return " + FIELD_NAME_$BINDINGS + ".values().toArray(new %s[" + FIELD_NAME_$BINDINGS + ".size()]);",
true
);
/**
*
*/
protected static final JavaMethod GET_DATA_BINDING_METHOD = newMethod(
PUBLIC,
JAXXBinding.class.getName(),
"getDataBinding",
"return " + FIELD_NAME_$BINDINGS + ".get(bindingId);",
true,
newArgument(TYPE_STRING, "bindingId")
);
/**
*
*/
protected static final JavaMethod FIRE_PROPERTY_CHANGE_METHOD = newMethod(
PUBLIC,
TYPE_VOID,
METHOD_NAME_FIRE_PROPERTY_CHANGE,
"super." + METHOD_NAME_FIRE_PROPERTY_CHANGE + "(propertyName, oldValue, newValue);",
true,
newArgument(TYPE_STRING, "propertyName"),
newArgument(TYPE_OBJECT, "oldValue"),
newArgument(TYPE_OBJECT, "newValue")
);
/**
*
*/
protected static final JavaMethod FIRE_PROPERTY_CHANGE_NAMED_METHOD = newMethod(
PUBLIC,
TYPE_VOID,
METHOD_NAME_FIRE_PROPERTY_CHANGE,
METHOD_NAME_$GET_PROPERTY_CHANGE_SUPPORT + "()." + METHOD_NAME_FIRE_PROPERTY_CHANGE + "(propertyName, oldValue, newValue);",
true,
newArgument(TYPE_STRING, "propertyName"),
newArgument(TYPE_OBJECT, "oldValue"),
newArgument(TYPE_OBJECT, "newValue")
);
/**
*
*/
protected static final JavaMethod GET_PROPERTY_CHANGE_SUPPORT_METHOD = newMethod(
0,
PropertyChangeSupport.class.getName(),
METHOD_NAME_$GET_PROPERTY_CHANGE_SUPPORT,
"if (" + FIELD_NAME_$PROPERTY_CHANGE_SUPPORT + " == null)\n" +
" " + FIELD_NAME_$PROPERTY_CHANGE_SUPPORT + " = new PropertyChangeSupport(this);\n" +
"return " + FIELD_NAME_$PROPERTY_CHANGE_SUPPORT + ";",
false
);
/**
*
*/
protected static final JavaMethod ADD_PROPERTY_CHANGE_SUPPORT_METHOD = newMethod(
PUBLIC,
TYPE_VOID,
"addPropertyChangeListener",
METHOD_NAME_$GET_PROPERTY_CHANGE_SUPPORT + "().addPropertyChangeListener(listener);",
true,
newArgument(PropertyChangeListener.class.getName(), "listener")
);
/**
*
*/
protected static final JavaMethod ADD_PROPERTY_CHANGE_SUPPORT_NAMED_METHOD = newMethod(
PUBLIC,
TYPE_VOID,
"addPropertyChangeListener",
METHOD_NAME_$GET_PROPERTY_CHANGE_SUPPORT + "().addPropertyChangeListener(property, listener);",
true,
newArgument(TYPE_STRING, "property"),
newArgument(PropertyChangeListener.class.getName(), "listener")
);
/**
*
*/
protected static final JavaMethod REMOVE_PROPERTY_CHANGE_SUPPORT_METHOD = newMethod(
PUBLIC,
TYPE_VOID,
"removePropertyChangeListener",
METHOD_NAME_$GET_PROPERTY_CHANGE_SUPPORT + "().removePropertyChangeListener(listener);",
true,
newArgument(PropertyChangeListener.class.getName(), "listener")
);
/**
*
*/
protected static final JavaMethod REMOVE_PROPERTY_CHANGE_SUPPORT_NAMED_METHOD = newMethod(
PUBLIC,
TYPE_VOID,
"removePropertyChangeListener",
METHOD_NAME_$GET_PROPERTY_CHANGE_SUPPORT + "().removePropertyChangeListener(property, listener);",
true,
newArgument(TYPE_STRING, "property"),
newArgument(PropertyChangeListener.class.getName(), "listener")
);
private static final String PARAMETER_NAME_$BINDING = "$binding";
/**
*
*/
protected static final JavaMethod PROCESS_DATA_BINDING_METHOD = newMethod(
PUBLIC,
TYPE_VOID,
METHOD_NAME_PROCESS_DATA_BINDING,
METHOD_NAME_PROCESS_DATA_BINDING + "(" + PARAMETER_NAME_$BINDING + ", false);",
true,
newArgument(TYPE_STRING, PARAMETER_NAME_$BINDING)
);
protected final JAXXBindingWriter>[] bindingWriters = new
JAXXBindingWriter[]{new SimpleJAXXObjectBindingWriter(),
new DefaultJAXXBindingWriter()
};
@Override
public boolean accept(JAXXCompiler compiler) {
// alwyas use the default finalizer
return true;
}
@Override
public void finalizeCompiler(CompiledObject root,
JAXXCompiler compiler,
JavaFile javaFile,
String packageName,
String className) throws ClassNotFoundException {
String fullClassName = packageName != null ?
packageName + "." + className : className;
if (root == null) {
throw new CompilerException("root tag can not be null");
}
ClassDescriptor superclass = root.getObjectClass();
boolean superclassIsJAXXObject =
compiler.isSuperClassAware(JAXXObject.class);
javaFile.setModifiers(PUBLIC);
javaFile.setName(fullClassName);
javaFile.setSimpleName(className);
javaFile.setSuperClass(JAXXCompiler.getCanonicalName(superclass));
javaFile.setSuperclassIsJAXXObject(superclassIsJAXXObject);
javaFile.addInterface(compiler.getExtraInterfaces());
javaFile.setAbstractClass(compiler.isAbstractClass());
javaFile.setGenericType(compiler.getGenericType());
javaFile.setSuperGenericType(compiler.getSuperGenericType());
if (!superclassIsJAXXObject) {
javaFile.addInterface(JAXXObject.class.getName());
}
}
@Override
public void prepareJavaFile(CompiledObject root,
JAXXCompiler compiler,
JavaFile javaFile,
String packageName,
String className) {
// add logger support if required
addLoggerSupport(compiler, javaFile, className);
// add JAXXObject support if required
addJAXXObjectSupport(compiler, root, javaFile);
addSimpleField(javaFile, SERIAL_VERSION_UID_FIELD);
addJAXXObjectDescriptorField(compiler, javaFile);
addMethod(javaFile, GET_JAXX_OBJECT_DESCRIPTOR_METHOD, JAXXUtil.class.getName());
addPreviousValuesField(compiler, javaFile, root);
DataBinding[] bindings = compiler.getBindingHelper().getDataBindings();
addInitializerMethod(compiler, javaFile);
addInitialize_01_createHandler(compiler, javaFile, className);
addInitialize_01_createComponents(compiler, javaFile);
addInitialize_02_registerActions(compiler, javaFile);
addInitialize_02_registerDataBindings(compiler, javaFile);
addInitialize_03_finalizeCreateComponents(compiler, javaFile);
addInitialize_04_applyDataBindings(compiler, javaFile);
addInitialize_05_setProperties(compiler, javaFile);
addInitialize_06_finalizeInitialize(compiler, javaFile);
javaFile.addBodyCode(compiler.getBodyCode().toString());
addDataBindings(compiler, javaFile, bindings);
addEventHandlers(compiler, javaFile);
}
protected void addDataBindings(JAXXCompiler compiler, JavaFile javaFile, DataBinding[] bindings) {
if (bindings.length < 1) {
// no data bindings
return;
}
// add import on each type of JAXXBinding used
for (JAXXBindingWriter> writer : bindingWriters) {
if (writer.isUsed()) {
javaFile.addImport(writer.getType());
}
}
// for each binding declare the constant Id
for (DataBinding binding : bindings) {
String constantId = binding.getConstantId();
// add the data binding constant Id
compiler.addSimpleField(newField(
(constantId.startsWith("BINDING_$") ? PRIVATE : PUBLIC) | FINAL | STATIC,
TYPE_STRING,
constantId,
false,
TypeManager.getJavaCode(binding.getRealId()))
);
}
}
protected void addJAXXObjectSupport(JAXXCompiler compiler, CompiledObject root, JavaFile javaFile) {
String jaxxContextImplementorClass =
compiler.getConfiguration().getJaxxContextClass().getName();
boolean superclassIsJAXXObject = javaFile.isSuperclassIsJAXXObject();
if (superclassIsJAXXObject) {
//FIXME tchemit 2011-01-30 : We should not add it (if user want to use it in script it must add the correct import
// Will be removed in version 3.0 with strict imports features
javaFile.addImport(JAXXContext.class);
} else {
// add JAXXObject support
addField(javaFile, OBJECT_MAP_FIELD);
addSimpleField(javaFile, BINDING_SOURCES_FIELD);
addSimpleField(javaFile, ACTIVE_BINDINGS_FIELD);
addSimpleField(javaFile, BINDINGS_FIELD);
javaFile.addMethod(GET_OBJECT_BY_ID_METHOD);
addMethod(javaFile, REGISTER_DATA_BINDING_METHOD);
addMethod(javaFile, GET_DATA_BINDINGS_METHOD, JAXXBinding.class.getSimpleName());
addMethod(javaFile, GET_DATA_BINDING_METHOD, JAXXBinding.class.getSimpleName());
javaFile.addMethod(createApplyDataBindingMethod());
javaFile.addMethod(createProcessDataBindingMethod());
javaFile.addMethod(createRemoveDataBindingMethod());
// JAXXContext
String type =
javaFile.getImportedType(jaxxContextImplementorClass);
javaFile.addField(newField(
PROTECTED | FINAL,
JAXXContext.class.getName(),
FIELD_NAME_DELEGATE_CONTEXT,
true,
"new " + type + "()")
);
javaFile.addImport(Container.class);
javaFile.addMethod(SET_CONTEXT_VALUE_METHOD);
javaFile.addMethod(SET_CONTEXT_VALUE_NAMED_METHOD);
javaFile.addMethod(GET_CONTEXT_VALUE_METHOD);
javaFile.addMethod(GET_CONTEXT_VALUE_NAMED_METHOD);
javaFile.addMethod(REMOVE_CONTEXT_VALUE_METHOD);
javaFile.addMethod(REMOVE_CONTEXT_VALUE_NAMED_METHOD);
addMethod(javaFile, GET_PARENT_CONTAINER_METHOD, SwingUtil.class.getName());
addMethod(javaFile, GET_PARENT_CONTAINER_MORE_METHOD, SwingUtil.class.getName());
// PropertyChangeSupport
addPropertyChangeSupport(root, javaFile);
// DataBinding
javaFile.addMethod(PROCESS_DATA_BINDING_METHOD);
}
}
protected void addLoggerSupport(JAXXCompiler compiler, JavaFile javaFile, String className) {
if (compiler.getConfiguration().isAddLogger()) {
javaFile.addSimpleField(newField(
PRIVATE | STATIC | FINAL,
Logger.class.getName(),
"log",
false,
"%s.getLogger(" + className + ".class)",
LogManager.class.getName()
)
);
}
}
/*---------------------------------------------------------------------------------*/
/*-- Create fields ----------------------------------------------------------------*/
/*---------------------------------------------------------------------------------*/
protected JavaField addJAXXObjectDescriptorField(JAXXCompiler compiler, JavaFile javaFile) {
JavaField field;
try {
JAXXObjectDescriptor descriptor = compiler.getJAXXObjectDescriptor();
String data = Base64Coder.serialize(descriptor, true);
/*ByteArrayOutputStream buffer = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(new GZIPOutputStream(buffer));
out.writeObject(descriptor);
out.close();
// the use of the weird deprecated constructor is deliberate -- we need to store the data as a String
// in the compiled class file, since byte array initialization is horribly inefficient compared to
// String initialization. So we store the bytes in the String, and we quite explicitly want a 1:1
// mapping between bytes and chars, with the high byte of the char set to zero. We can then safely
// reconstitute the original byte[] at a later date. This is unquestionably an abuse of the String
// type, but if we could efficiently store a byte[] we wouldn't have to do this.
String data = new String(buffer.toByteArray(), 0);*/
int sizeLimit = 65000; // constant strings are limited to 64K, and I'm not brave enough to push right up to the limit
if (data.length() < sizeLimit) {
field = newField(PRIVATE | STATIC | FINAL,
TYPE_STRING,
FIELD_NAME_$JAXX_OBJECT_DESCRIPTOR,
false,
TypeManager.getJavaCode(data)
);
} else {
StringBuilder initializer = new StringBuilder();
for (int i = 0; i < data.length(); i += sizeLimit) {
String name = FIELD_NAME_$JAXX_OBJECT_DESCRIPTOR + i;
javaFile.addField(newField(
PRIVATE | STATIC,
TYPE_STRING,
name,
false,
TypeManager.getJavaCode(data.substring(i, Math.min(i + sizeLimit, data.length()))))
);
if (initializer.length() > 0) {
initializer.append(" + ");
}
initializer.append(TYPE_STRING + ".valueOf(");
initializer.append(name);
initializer.append(")");
}
field = newField(PRIVATE | STATIC | FINAL,
TYPE_STRING,
FIELD_NAME_$JAXX_OBJECT_DESCRIPTOR,
false,
initializer.toString()
);
}
} catch (IOException e) {
throw new RuntimeException("Internal error: can't-happen error", e);
}
javaFile.addSimpleField(field);
return field;
}
protected void addPreviousValuesField(JAXXCompiler compiler, JavaFile javaFile, CompiledObject root) {
boolean superclassIsJAXXObject = javaFile.isSuperclassIsJAXXObject();
if (compiler.getStylesheet() != null) {
boolean needField = true;
if (superclassIsJAXXObject) {
// check alreay exists on parent
ClassDescriptor superclass = root.getObjectClass();
if (log.isDebugEnabled()) {
log.debug("superclass : " + superclass);
}
JAXXCompiler parentCompiler =
compiler.getEngine().getJAXXCompiler(
superclass.getName()
);
if (parentCompiler != null) {
needField = parentCompiler.getStylesheet() == null;
} else {
try {
superclass.getDeclaredFieldDescriptor(
PREVIOUS_VALUES_FIELD.getName());
needField = false;
} catch (NoSuchFieldException ex) {
// field not found
}
}
if (needField && log.isDebugEnabled()) {
log.debug("no " + PREVIOUS_VALUES_FIELD.getName() +
" field in super class");
}
}
if (needField) {
addSimpleField(javaFile, PREVIOUS_VALUES_FIELD);
}
}
}
/*---------------------------------------------------------------------------------*/
/*-- Create methods ---------------------------------------------------------------*/
/*---------------------------------------------------------------------------------*/
protected void addPropertyChangeSupport(CompiledObject root, JavaFile javaFile) {
ClassDescriptor currentClass = root.getObjectClass();
MethodDescriptor firePropertyChange = null;
while (firePropertyChange == null && currentClass != null) {
try {
firePropertyChange = currentClass.getDeclaredMethodDescriptor(
METHOD_NAME_FIRE_PROPERTY_CHANGE,
ClassDescriptorHelper.getClassDescriptor(String.class),
ClassDescriptorHelper.getClassDescriptor(Object.class),
ClassDescriptorHelper.getClassDescriptor(Object.class)
);
} catch (NoSuchMethodException e) {
currentClass = currentClass.getSuperclass();
}
}
int modifiers = firePropertyChange != null ?
firePropertyChange.getModifiers() : 0;
if (isPublic(modifiers)) {
// we have all the support we need
return;
}
if (isProtected(modifiers)) {
// there is property change support but the firePropertyChange method is protected
javaFile.addMethod(FIRE_PROPERTY_CHANGE_METHOD);
} else {
addField(javaFile, PROPERTY_CHANGE_SUPPORT_FIELD);
addMethod(javaFile, GET_PROPERTY_CHANGE_SUPPORT_METHOD, PropertyChangeSupport.class.getName());
addMethod(javaFile, ADD_PROPERTY_CHANGE_SUPPORT_METHOD);
addMethod(javaFile, ADD_PROPERTY_CHANGE_SUPPORT_NAMED_METHOD);
addMethod(javaFile, REMOVE_PROPERTY_CHANGE_SUPPORT_METHOD);
addMethod(javaFile, REMOVE_PROPERTY_CHANGE_SUPPORT_NAMED_METHOD);
addMethod(javaFile, FIRE_PROPERTY_CHANGE_NAMED_METHOD);
}
}
protected void addInitialize_01_createHandler(JAXXCompiler compiler, JavaFile javaFile, String className) {
String handlerReturnType = String.format("%s extends %s>", compiler.getImportedType(UIHandler.class.getName()), compiler.getImportedType(JAXXObject.class.getName()));
if (compiler.isAbstractClass()) {
// abstract class can not define handler
// declare abstract method
JavaMethod method = newMethod(PROTECTED | ABSTRACT,
handlerReturnType,
METHOD_NAME_$INITIALIZE_01_CREATE_HANDLER,
"",
javaFile.isSuperclassIsJAXXObject()
);
javaFile.addMethod(method);
return;
}
StringBuilder body = new StringBuilder();
body.append(JavaFileGenerator.addDebugLoggerInvocation(compiler, "this"));
String eol = JAXXCompiler.getLineSeparator();
ClassDescriptor objectClass = compiler.getRootObject().getObjectClass();
// boolean useHandler = compiler.isUseHandler() && (!compiler.isAbstractClass() || compiler.getCompiledObject("handler") != null);
boolean useHandler = compiler.isUseHandler();
if (javaFile.isSuperclassIsJAXXObject()) {
if (!useHandler && objectClass.tryToGetFieldDescriptor("handler").isPresent()) {
// found a handler in super class, nothing to generate
return;
// // just override it
// String handlerReturnType = String.format("%s<%s>", compiler.getImportedType(UIHandler.class.getName()), objectClass.getSimpleName());
// body.append(String.format("return super.%s();", METHOD_NAME_$INITIALIZE_01_CREATE_HANDLER)).append(eol);
// JavaMethod method = newMethod(PROTECTED,
// handlerReturnType,
// METHOD_NAME_$INITIALIZE_01_CREATE_HANDLER,
// body.toString(),
// javaFile.isSuperclassIsJAXXObject()
// );
// javaFile.addMethod(method);
// return true;
}
}
if (!useHandler) {
body.append("return null;").append(eol);
JavaMethod method = newMethod(PROTECTED,
handlerReturnType,
METHOD_NAME_$INITIALIZE_01_CREATE_HANDLER,
body.toString(),
javaFile.isSuperclassIsJAXXObject()
);
javaFile.addMethod(method);
return;
}
String handler = compiler.getUiHandler();
String handlerType = compiler.getImportedType(handler);
body.append("return handler = new ").append(handlerType);
String genericType = compiler.getGenericType();
if (genericType != null) {
body.append("<>");
if (!compiler.isAbstractClass()) {
handlerReturnType = handlerType;
} else {
handlerReturnType = String.format("%s<%s<%s>>", compiler.getImportedType(UIHandler.class.getName()), className, genericType);
}
}
String constructorParams = "";
try {
ClassDescriptor classDescriptor = ClassDescriptorHelper.getClassDescriptor(handler);
MethodDescriptor[] constructorDescriptors = classDescriptor.getConstructorDescriptors();
if (constructorDescriptors.length == 1
&& constructorDescriptors[0].getParameterTypes().length == 1
&& constructorDescriptors[0].getParameterTypes()[0] != null) {
if (constructorDescriptors[0].getParameterTypes()[0].isAssignableFrom(objectClass)
|| javaFile.getName().equals(constructorDescriptors[0].getParameterTypes()[0].getName())
|| constructorDescriptors[0].getParameterTypes()[0].isAssignableFrom(ClassDescriptorHelper.getClassDescriptor(JAXXObject.class))) {
constructorParams = "this";
}
}
} catch (ClassNotFoundException e) {
// silent
}
body.append("(").append(constructorParams).append(");").append(eol);
JavaMethod method = newMethod(PROTECTED,
handlerReturnType,
METHOD_NAME_$INITIALIZE_01_CREATE_HANDLER,
body.toString(),
javaFile.isSuperclassIsJAXXObject()
);
javaFile.addMethod(method);
}
protected void addInitialize_01_createComponents(JAXXCompiler compiler, JavaFile javaFile) {
StringBuilder body = new StringBuilder();
body.append(JavaFileGenerator.addDebugLoggerInvocation(compiler, "this"));
String eol = JAXXCompiler.getLineSeparator();
if (javaFile.isSuperclassIsJAXXObject()) {
body.append(String.format("super.%s();", METHOD_NAME_$INITIALIZE_01_CREATE_COMPONENTS)).append(eol);
}
CompiledObject root = compiler.getRootObject();
String rootId = root.getId();
body.append(FIELD_NAME_$OBJECT_MAP + ".put(").append(TypeManager.getJavaCode(rootId)).append(", ").append(rootId).append(");").append(eol);
Iterator i = compiler.getObjectCreationOrder();
boolean lastWasMethodCall = false;
//TODO-TC20091025 should do init of root first ?
// root.getDecorator().createInitializer(compiler, root, root, code, lastWasMethodCall);
while (i.hasNext()) {
CompiledObject object = i.next();
if (object == root) {
continue;
}
CompiledObjectDecorator decorator = object.getDecorator();
lastWasMethodCall = decorator.createInitializer(compiler, root, object, body, lastWasMethodCall);
}
root.getDecorator().createInitializer(compiler, root, root, body, lastWasMethodCall);
if (compiler.getInitializer().length() > 0) {
body.append(compiler.getInitializer());
}
JavaMethod method = newMethod(PROTECTED,
TYPE_VOID,
METHOD_NAME_$INITIALIZE_01_CREATE_COMPONENTS,
body.toString(),
javaFile.isSuperclassIsJAXXObject()
);
javaFile.addMethod(method);
}
protected void addInitialize_02_registerDataBindings(JAXXCompiler compiler, JavaFile javaFile) {
StringBuilder body = new StringBuilder();
body.append(JavaFileGenerator.addDebugLoggerInvocation(compiler, "this"));
String eol = JAXXCompiler.getLineSeparator();
if (javaFile.isSuperclassIsJAXXObject()) {
body.append(String.format("super.%s();", METHOD_NAME_$INITIALIZE_02_REGISTER_DATA_BINDINGS)).append(eol);
}
JavaFileGenerator generator = new JavaFileGenerator(eol, true);
//TODO use optimized writer for simple cases
for (JAXXBindingWriter> writer : bindingWriters) {
writer.reset();
}
// add import on each type of JAXXBinding used
for (JAXXBindingWriter> writer : bindingWriters) {
if (writer.isUsed()) {
compiler.getJavaFile().addImport(writer.getType());
}
}
DataBinding[] bindings = compiler.getBindingHelper().getDataBindings();
body.append("// register ");
body.append(bindings.length);
body.append(" data bindings");
body.append(eol);
for (DataBinding binding : bindings) {
for (JAXXBindingWriter> writer : bindingWriters) {
if (writer.accept(binding)) {
writer.write(binding, generator, body);
break;
}
}
}
JavaMethod method = newMethod(PROTECTED,
TYPE_VOID,
METHOD_NAME_$INITIALIZE_02_REGISTER_DATA_BINDINGS,
body.toString(),
javaFile.isSuperclassIsJAXXObject()
);
javaFile.addMethod(method);
}
protected void addInitialize_02_registerActions(JAXXCompiler compiler, JavaFile javaFile) {
StringBuilder body = new StringBuilder();
body.append(JavaFileGenerator.addDebugLoggerInvocation(compiler, "this"));
String eol = JAXXCompiler.getLineSeparator();
if (javaFile.isSuperclassIsJAXXObject()) {
body.append(String.format("super.%s();", METHOD_NAME_$INITIALIZE_03_REGISTER_ACTIONS)).append(eol);
}
body.append(compiler.getActionsInitializer()).append(eol);
JavaMethod method = newMethod(PROTECTED,
TYPE_VOID,
METHOD_NAME_$INITIALIZE_03_REGISTER_ACTIONS,
body.toString(),
javaFile.isSuperclassIsJAXXObject()
);
javaFile.addMethod(method);
}
protected void addInitialize_03_finalizeCreateComponents(JAXXCompiler compiler, JavaFile javaFile) {
StringBuilder body = new StringBuilder();
body.append(JavaFileGenerator.addDebugLoggerInvocation(compiler, "this"));
String eol = JAXXCompiler.getLineSeparator();
if (javaFile.isSuperclassIsJAXXObject()) {
body.append(String.format("super.%s();", METHOD_NAME_$INITIALIZE_03_FINALIZE_CREATE_COMPONENTS)).append(eol);
}
for (CompiledObject object : compiler.getObjects().values()) {
CompiledObjectDecorator decorator = object.getDecorator();
body.append(decorator.createCompleteSetupMethod(compiler,
object,
javaFile)
);
}
JavaMethod method = newMethod(PROTECTED,
TYPE_VOID,
METHOD_NAME_$INITIALIZE_03_FINALIZE_CREATE_COMPONENTS,
body.toString(),
javaFile.isSuperclassIsJAXXObject()
);
javaFile.addMethod(method);
}
protected void addInitialize_04_applyDataBindings(JAXXCompiler compiler, JavaFile javaFile) {
StringBuilder body = new StringBuilder();
String eol = JAXXCompiler.getLineSeparator();
body.append(JavaFileGenerator.addDebugLoggerInvocation(compiler, "this"));
if (javaFile.isSuperclassIsJAXXObject()) {
body.append(String.format("super.%s();", METHOD_NAME_$INITIALIZE_04_APPLY_DATA_BINDINGS)).append(eol);
} else {
DataBinding[] bindings = compiler.getBindingHelper().getDataBindings();
if (bindings.length > 0) {
body.append(eol);
body.append("// apply ");
body.append(bindings.length);
body.append(" data bindings");
body.append(eol);
body.append(JAXXUtil.class.getSimpleName());
body.append("." + METHOD_NAME_APPLY_DATA_BINDING + "(this, " + FIELD_NAME_$BINDINGS + ".keySet());");
body.append(eol);
}
}
JavaMethod method = newMethod(PROTECTED,
TYPE_VOID,
METHOD_NAME_$INITIALIZE_04_APPLY_DATA_BINDINGS,
body.toString(),
javaFile.isSuperclassIsJAXXObject()
);
javaFile.addMethod(method);
}
protected void addInitialize_05_setProperties(JAXXCompiler compiler, JavaFile javaFile) {
StringBuilder body = new StringBuilder();
String eol = JAXXCompiler.getLineSeparator();
body.append(JavaFileGenerator.addDebugLoggerInvocation(compiler, "this"));
if (javaFile.isSuperclassIsJAXXObject()) {
body.append(String.format("super.%s();", METHOD_NAME_$INITIALIZE_05_SET_PROPERTIES)).append(eol);
}
DataBinding[] bindings = compiler.getBindingHelper().getSimpleBindings();
if (bindings.length > 0) {
StringBuilder initCode = new StringBuilder();
for (DataBinding binding : bindings) {
String binding1 = binding.getInitDataBinding();
if (binding1 != null && !binding1.trim().isEmpty()) {
initCode.append(binding1);
}
}
if (initCode.length() > 0) {
body.append(eol);
body.append("// apply ");
body.append(bindings.length);
body.append(" property setters");
body.append(eol);
body.append(initCode.toString().trim());
}
}
if (compiler.getLateInitializer().length() > 0) {
body.append("// late initializer").append(eol);
body.append(compiler.getLateInitializer()).append(eol);
}
JavaMethod method = newMethod(PROTECTED,
TYPE_VOID,
METHOD_NAME_$INITIALIZE_05_SET_PROPERTIES,
body.toString(),
javaFile.isSuperclassIsJAXXObject()
);
javaFile.addMethod(method);
}
protected void addInitialize_06_finalizeInitialize(JAXXCompiler compiler, JavaFile javaFile) {
StringBuilder body = new StringBuilder();
body.append(JavaFileGenerator.addDebugLoggerInvocation(compiler, "this"));
String eol = JAXXCompiler.getLineSeparator();
if (javaFile.isSuperclassIsJAXXObject()) {
body.append(String.format("super.%s();", METHOD_NAME_$INITIALIZE_06_FINALIZE_INITIALIZE)).append(eol);
}
JavaMethod method = newMethod(PROTECTED,
TYPE_VOID,
METHOD_NAME_$INITIALIZE_06_FINALIZE_INITIALIZE,
body.toString(),
javaFile.isSuperclassIsJAXXObject()
);
javaFile.addMethod(method);
}
protected void addEventHandlers(JAXXCompiler compiler,
JavaFile javaFile) {
for (Map.Entry>> e1 : compiler.getEventHandlers().entrySet()) {
// outer loop is iterating over different objects (well, technically, different Java expressions)
for (Map.Entry> e2 : e1.getValue().entrySet()) {
// iterate over different types of listeners for this particular object (MouseListener, ComponentListener, etc.)
for (EventHandler handler : e2.getValue()) {
// iterate over individual event handlers of a single type
String methodName = compiler.getEventHandlerMethodName(handler);
MethodDescriptor listenerMethod = handler.getListenerMethod();
if (listenerMethod.getParameterTypes().length != 1) {
throw new CompilerException(
"Expected event handler " +
listenerMethod.getName() + " of class " +
handler.getListenerClass() +
" to have exactly one argument"
);
}
ClassDescriptor eventType =
listenerMethod.getParameterTypes()[0];
JavaArgument argument =
JavaElementFactory.newArgument(
JAXXCompiler.getCanonicalName(eventType),
"event"
);
String body = JavaFileGenerator.addDebugLoggerInvocation(compiler, "event");
body += handler.getJavaCode();
javaFile.addMethod(JavaElementFactory.newMethod(
PUBLIC,
TYPE_VOID,
methodName,
body,
false,
argument)
);
}
}
}
}
public void addInitializerMethod(JAXXCompiler compiler, JavaFile javaFile) {
String eol = JAXXCompiler.getLineSeparator();
StringBuilder code = new StringBuilder();
code.append(JavaFileGenerator.addDebugLoggerInvocation(compiler, "this"));
code.append(compiler.getRootObject().getId()).append(" = this;").append(eol);
if (javaFile.isSuperclassIsJAXXObject()) {
code.append("super." + METHOD_NAME_$INITIALIZE + "();").append(eol);
} else {
code.append(String.format(METHOD_$INITIALIZER_CALL,
eol,
METHOD_NAME_$INITIALIZE_01_CREATE_HANDLER,
METHOD_NAME_$INITIALIZE_01_CREATE_COMPONENTS,
METHOD_NAME_$INITIALIZE_02_REGISTER_DATA_BINDINGS,
METHOD_NAME_$INITIALIZE_03_FINALIZE_CREATE_COMPONENTS,
METHOD_NAME_$INITIALIZE_03_REGISTER_ACTIONS,
METHOD_NAME_$INITIALIZE_04_APPLY_DATA_BINDINGS,
METHOD_NAME_$INITIALIZE_05_SET_PROPERTIES,
METHOD_NAME_$INITIALIZE_06_FINALIZE_INITIALIZE
));
}
JavaMethod method = JavaElementFactory.newMethod(PROTECTED,
TYPE_VOID,
METHOD_NAME_$INITIALIZE,
code.toString(),
javaFile.isSuperclassIsJAXXObject()
);
javaFile.addMethod(method);
}
protected JavaMethod createApplyDataBindingMethod() {
StringBuilder buffer = new StringBuilder();
String eol = JAXXCompiler.getLineSeparator();
buffer.append("if (" + FIELD_NAME_$BINDINGS + ".containsKey(" + PARAMETER_NAME_$BINDING + ")) {");
buffer.append(eol);
buffer.append(" getDataBinding(" + PARAMETER_NAME_$BINDING + ")." + METHOD_NAME_APPLY_DATA_BINDING + "();");
buffer.append(eol);
buffer.append("}");
buffer.append(eol);
buffer.append(METHOD_NAME_PROCESS_DATA_BINDING + "(" + PARAMETER_NAME_$BINDING + ");");
return JavaElementFactory.newMethod(
PUBLIC,
TYPE_VOID,
METHOD_NAME_APPLY_DATA_BINDING,
buffer.toString(),
true,
JavaElementFactory.newArgument(TYPE_STRING, PARAMETER_NAME_$BINDING)
);
}
protected JavaMethod createRemoveDataBindingMethod() {
StringBuilder buffer = new StringBuilder();
String eol = JAXXCompiler.getLineSeparator();
buffer.append("if (" + FIELD_NAME_$BINDINGS + ".containsKey(" + PARAMETER_NAME_$BINDING + ")) {");
buffer.append(eol);
buffer.append(" getDataBinding(" + PARAMETER_NAME_$BINDING + ")." + METHOD_NAME_REMOVE_DATA_BINDING + "();");
buffer.append(eol);
buffer.append("}");
return JavaElementFactory.newMethod(
PUBLIC,
TYPE_VOID,
METHOD_NAME_REMOVE_DATA_BINDING,
buffer.toString(),
true,
JavaElementFactory.newArgument(TYPE_STRING, PARAMETER_NAME_$BINDING)
);
}
protected JavaMethod createProcessDataBindingMethod() {
StringBuilder code = new StringBuilder();
String eol = JAXXCompiler.getLineSeparator();
// the force parameter forces the update to happen even if it is already in activeBindings. This
// is used on superclass invocations b/c by the time the call gets to the superclass, it is already
// marked active and would otherwise be skipped
code.append(" if (!$force && " + FIELD_NAME_$ACTIVE_BINDINGS + ".contains(" + PARAMETER_NAME_$BINDING + ")) { ");
code.append(eol);
code.append(" return;");
code.append(eol);
code.append("}").append(eol);
code.append(FIELD_NAME_$ACTIVE_BINDINGS + ".add(" + PARAMETER_NAME_$BINDING + ");");
code.append(eol);
code.append("try {").append(eol);
code.append(" if (" + FIELD_NAME_$BINDINGS + ".containsKey(" + PARAMETER_NAME_$BINDING + ")) {");
code.append(eol);
code.append(" getDataBinding(" + PARAMETER_NAME_$BINDING + ")." + METHOD_NAME_PROCESS_DATA_BINDING + "();");
code.append(eol);
code.append(" }").append(eol);
code.append("} finally {").append(eol);
code.append(" " + FIELD_NAME_$ACTIVE_BINDINGS + ".remove(" + PARAMETER_NAME_$BINDING + ");");
code.append(eol);
code.append("}").append(eol);
return JavaElementFactory.newMethod(
PUBLIC,
TYPE_VOID,
METHOD_NAME_PROCESS_DATA_BINDING,
code.toString(),
true,
newArgument(TYPE_STRING, PARAMETER_NAME_$BINDING),
newArgument(TYPE_BOOLEAN, "$force")
);
}
}