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

jaxx.compiler.finalizers.DefaultFinalizer Maven / Gradle / Ivy

There is a newer version: 3.0-alpha-6
Show newest version
/*
 * #%L
 * JAXX :: Compiler
 * %%
 * Copyright (C) 2008 - 2014 Code Lutin, Tony Chemit
 * %%
 * 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 jaxx.compiler.finalizers;

import jaxx.compiler.CompiledObject;
import jaxx.compiler.CompiledObjectDecorator;
import jaxx.compiler.CompilerException;
import jaxx.compiler.EventHandler;
import jaxx.compiler.JAXXCompiler;
import jaxx.compiler.binding.DataBinding;
import jaxx.compiler.binding.writers.DefaultJAXXBindingWriter;
import jaxx.compiler.binding.writers.JAXXBindingWriter;
import jaxx.compiler.binding.writers.SimpleJAXXObjectBindingWriter;
import jaxx.compiler.java.JavaArgument;
import jaxx.compiler.java.JavaElementFactory;
import jaxx.compiler.java.JavaField;
import jaxx.compiler.java.JavaFile;
import jaxx.compiler.java.JavaFileGenerator;
import jaxx.compiler.java.JavaMethod;
import jaxx.compiler.reflect.ClassDescriptor;
import jaxx.compiler.reflect.ClassDescriptorHelper;
import jaxx.compiler.reflect.MethodDescriptor;
import jaxx.compiler.types.TypeManager;
import jaxx.runtime.Base64Coder;
import jaxx.runtime.JAXXBinding;
import jaxx.runtime.JAXXContext;
import jaxx.runtime.JAXXObject;
import jaxx.runtime.JAXXObjectDescriptor;
import jaxx.runtime.JAXXUtil;
import jaxx.runtime.SwingUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

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.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 jaxx.compiler.java.JavaElementFactory.newArgument;
import static jaxx.compiler.java.JavaElementFactory.newField;
import static 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]
 * @plexus.component role-hint="default" role="jaxx.compiler.finalizers.JAXXCompilerFinalizer"
 */
public class DefaultFinalizer extends AbstractFinalizer {

    /** Logger. */
    protected static final Log log = LogFactory.getLog(DefaultFinalizer.class);

    private static final String PARAMETER_NAME_$BINDING = "$binding";

    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_ALL_COMPONENTS_CREATED = "allComponentsCreated";

    public static final String FIELD_NAME_CONTEXT_INITIALIZED = "contextInitialized";

    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_JAXXOBJECT_DESCRIPTOR = "$getJAXXObjectDescriptor";

    public static final String METHOD_NAME_$REGISTER_DEFAULT_BINDINGS = "$registerDefaultBindings";

    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_$COMPLETE_SETUP = "$completeSetup";

    public static final String METHOD_NAME_$AFTER_COMPLETE_SETUP = "$afterCompleteSetup";

    /** 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 ALL_COMPONENTS_CREATED_FIELD = newField(
            PRIVATE,
            TYPE_BOOLEAN,
            FIELD_NAME_ALL_COMPONENTS_CREATED,
            false
    );

    /**
     *
     */
    protected static final JavaField CONTEXT_INITIALIZED = newField(
            PRIVATE,
            TYPE_BOOLEAN,
            FIELD_NAME_CONTEXT_INITIALIZED,
            false,
            "true"
    );

    /**
     *
     */
    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_JAXXOBJECT_DESCRIPTOR,
            "return %s.decodeCompressedJAXXObjectDescriptor(" +
            FIELD_NAME_$JAXX_OBJECT_DESCRIPTOR + ");",
            false
    );

    /**
     *
     */
    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 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_PARENT_CONTEXT = "parentContext";

    public static final String METHOD_NAME$BEFORE_INIT = "beforeInit";

    private static final String METHOD_NAME$AFTER_INIT = "afterInit";

    @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) throws ClassNotFoundException {

        // add logger support if required
        addLoggerSupport(compiler, javaFile, className);

        // add JAXXObject support if required
        addJAXXObjectSupport(compiler, root, javaFile);

        addSimpleField(javaFile, SERIAL_VERSION_UID_FIELD);
        addSimpleField(javaFile, ALL_COMPONENTS_CREATED_FIELD);

        addJAXXObjectDescriptorField(compiler, javaFile);
        addMethod(javaFile,
                  GET_JAXX_OBJECT_DESCRIPTOR_METHOD,
                  JAXXUtil.class.getName()
        );

        addPreviousValuesField(compiler, javaFile, root);

//        addConstructors(compiler, javaFile, className);

        DataBinding[] bindings = compiler.getBindingHelper().getDataBindings();

        addInitializerMethod(compiler, javaFile, bindings.length);

        javaFile.addBodyCode(compiler.getBodyCode().toString());

        addDataBindings(compiler, javaFile, bindings);

        addCompleteSetupMethod(compiler, javaFile);

        addEventHandlers(compiler, javaFile);
    }

    protected void addDataBindings(JAXXCompiler compiler,
                                   JavaFile javaFile,
                                   DataBinding[] bindings) {

        if (bindings.length < 1) {
            // no data bindings
            return;
        }

        // create the $registerDefaultBindings method

        for (JAXXBindingWriter writer : bindingWriters) {
            writer.reset();
        }
        addRegisteredDefaultBindingsMethod(compiler, javaFile);

        // add import on each type of JAXXBinding used

        for (JAXXBindingWriter writer : bindingWriters) {
            if (writer.isUsed()) {
                compiler.getJavaFile().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 addConstructors(JAXXCompiler compiler,
                                   JavaFile javaFile,
                                   String className) {

        boolean superclassIsJAXXObject = javaFile.isSuperclassIsJAXXObject();

        //TC 20090228 - only generate constructors if not done in scripts
        boolean constructorDetected = false;
        MethodDescriptor[] methods = compiler.getScriptMethods();
        for (MethodDescriptor m : methods) {
            try {
                m.getReturnType();
                if (className.equals(m.getName())) {
                    constructorDetected = true;
                    break;
                }
            } catch (Exception e) {
                log.warn("could not find return type " + m);
            }
        }
        if (!constructorDetected) {

            //creates default constructors

            JavaMethod constructor = createConstructor(compiler,
                                                       className,
                                                       superclassIsJAXXObject
            );
            javaFile.addMethod(constructor);
            constructor = createConstructorWithInitialContext(
                    compiler,
                    className,
                    superclassIsJAXXObject
            );
            javaFile.addMethod(constructor);
        }
    }

    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());
//            javaFile.addMethod(GET_PARENT_CONTAINER_METHOD);
//            javaFile.addMethod(GET_PARENT_CONTAINER_MORE_METHOD);

            // 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.addImport(LogFactory.class);

            javaFile.addSimpleField(newField(
                                            PRIVATE | STATIC | FINAL,
                                            Log.class.getName(),
                                            "log",
                                            false,
                                            "%s.getLog(" + className + ".class)",
                                            LogFactory.class.getName()
                                    )
            );
        }
    }

    protected final JAXXBindingWriter[] bindingWriters = new
            JAXXBindingWriter[]{new SimpleJAXXObjectBindingWriter(),
                                new DefaultJAXXBindingWriter()
    };

    /*---------------------------------------------------------------------------------*/
    /*-- 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 JavaMethod addRegisteredDefaultBindingsMethod(JAXXCompiler compiler,
                                                            JavaFile javaFile) {
        DataBinding[] bindings = compiler.getBindingHelper().getDataBindings();
        StringBuilder initCode = new StringBuilder();

        String eol = JAXXCompiler.getLineSeparator();
        JavaFileGenerator generator = new JavaFileGenerator(eol, true);
        //TODO use optimized writer for simple cases

        initCode.append("// register ");
        initCode.append(bindings.length);
        initCode.append(" data bindings");
        initCode.append(eol);

        for (DataBinding binding : bindings) {

            for (JAXXBindingWriter writer : bindingWriters) {
                if (writer.accept(binding)) {
                    writer.write(binding, generator, initCode);
                    break;
                }
            }
        }
        JavaMethod method = newMethod(PRIVATE,
                                      TYPE_VOID,
                                      METHOD_NAME_$REGISTER_DEFAULT_BINDINGS,
                                      initCode.toString(),
                                      false
        );
        javaFile.addMethod(method);
        return 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)
                    );
                }
            }
        }
    }

    protected JavaMethod createConstructor(JAXXCompiler compiler,
                                           String className,
                                           boolean superclassIsJAXXObject) throws CompilerException {
        StringBuilder code = new StringBuilder();
        String constructorParams = compiler.getRootObject().getConstructorParams();
        String eol = JAXXCompiler.getLineSeparator();
        if (constructorParams != null) {
            code.append("        super(");
            code.append(constructorParams);
            code.append(");");
            code.append(eol);
        } else {
//            if (superclassIsJAXXObject) {
//                code.append("        super();").append(eol);
//            }
        }
        code.append(METHOD_NAME_$INITIALIZE + "();");
        code.append(eol);
        return JavaElementFactory.newMethod(PUBLIC,
                                            null,
                                            className,
                                            code.toString(),
                                            false
        );
    }

    protected JavaMethod createConstructorWithInitialContext(JAXXCompiler compiler,
                                                             String className,
                                                             boolean superclassIsJAXXObject) throws CompilerException {
        StringBuilder code = new StringBuilder();
        String constructorParams = compiler.getRootObject().getConstructorParams();
        String eol = JAXXCompiler.getLineSeparator();
        String realConstructorParams = "";
        if (superclassIsJAXXObject) {
            realConstructorParams = PARAMETER_NAME_PARENT_CONTEXT;
        }
        if (constructorParams != null) {

            if (superclassIsJAXXObject) {
                realConstructorParams += " ,";
            }
            realConstructorParams += constructorParams;
        }

        realConstructorParams = realConstructorParams.trim();

        if (StringUtils.isNotEmpty(realConstructorParams)) {

            // add a super invocation
            code.append("        super(");
            code.append(realConstructorParams);
            code.append(");");
            code.append(eol);
        }


//        if (constructorParams != null) {
//            if (superclassIsJAXXObject) {
//                //TODO-TC20091127 This MUST be a convention in JAXX : all specialized constructor must a second
//                //TODO constructor with extra first parameter as parentContext
//                constructorParams = PARAMETER_NAME_PARENT_CONTEXT + ", " + constructorParams;
//            }
//            code.append("        super(");
//            code.append(constructorParams);
//            code.append(");");
//            code.append(eol);
//        } else {
//            if (superclassIsJAXXObject) {
//                code.append("        super(" + PARAMETER_NAME_PARENT_CONTEXT);
//                code.append(");");
//                code.append(eol);
//            }
//        }
        if (!superclassIsJAXXObject) {
            String prefix = compiler.getImportedType(JAXXUtil.class);
            code.append(prefix);
            code.append(".initContext(this, " + PARAMETER_NAME_PARENT_CONTEXT + ");");
            code.append(eol);
        }
        code.append(METHOD_NAME_$INITIALIZE + "();");
        code.append(eol);
        JavaArgument argument = JavaElementFactory.newArgument(
                JAXXContext.class.getName(),
                PARAMETER_NAME_PARENT_CONTEXT
        );
        return JavaElementFactory.newMethod(PUBLIC,
                                            null,
                                            className,
                                            code.toString(),
                                            false,
                                            argument
        );
    }

    public JavaMethod addInitializerMethod(JAXXCompiler compiler,
                                           JavaFile javaFile,
                                           int nbBindings) throws CompilerException {

        boolean overrideContextInitialized =
                compiler.containsScriptField(FIELD_NAME_CONTEXT_INITIALIZED);

        String eol = JAXXCompiler.getLineSeparator();
        StringBuilder code = new StringBuilder();
        CompiledObject root = compiler.getRootObject();
        code.append("if (" + FIELD_NAME_ALL_COMPONENTS_CREATED);
        if (overrideContextInitialized) {
            code.append("|| !" + FIELD_NAME_CONTEXT_INITIALIZED);
        }
        code.append(") {");
        code.append(eol);
        code.append("    return;").append(eol);
        code.append("}").append(eol);
        code.append(JavaFileGenerator.addDebugLoggerInvocation(compiler, "this"));
        boolean useHandler = compiler.isUseHandler();

        if (useHandler) {
            code.append("handler." + METHOD_NAME$BEFORE_INIT + "(this);").append(eol);
        }
        code.append(FIELD_NAME_$OBJECT_MAP + ".put(");
        String rootId = root.getId();
        code.append(TypeManager.getJavaCode(rootId));
//        code.append(", this);");
        code.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,
                                                            code,
                                                            lastWasMethodCall
            );
        }
        root.getDecorator().createInitializer(compiler,
                                              root,
                                              root,
                                              code,
                                              lastWasMethodCall
        );
        if (compiler.getInitializer().length() > 0) {
            code.append(compiler.getInitializer());
        }
        // register bindings before anything else
        if (nbBindings > 0) {
            // ajout invocation a la methode d'enregistrement des bindings
            code.append("// registers ");
            code.append(nbBindings);
            code.append(" data bindings");
            code.append(eol);
            code.append(METHOD_NAME_$REGISTER_DEFAULT_BINDINGS + "();");
            code.append(eol);
        }
        code.append(METHOD_NAME_$COMPLETE_SETUP + "();");
        code.append(eol);

        if (useHandler) {
            code.append("handler." + METHOD_NAME$AFTER_INIT + "(this);").append(eol);
        }
        JavaMethod method = JavaElementFactory.newMethod(PRIVATE,
                                                         TYPE_VOID,
                                                         METHOD_NAME_$INITIALIZE,
                                                         code.toString(),
                                                         false
        );
        javaFile.addMethod(method);
        return method;
    }

    protected JavaMethod addCompleteSetupMethod(JAXXCompiler compiler,
                                                JavaFile javaFile) {
        StringBuilder code = new StringBuilder();
        code.append(FIELD_NAME_ALL_COMPONENTS_CREATED + " = true;");
        String eol = JAXXCompiler.getLineSeparator();
        code.append(eol);
        code.append(JavaFileGenerator.addDebugLoggerInvocation(compiler, "this"));
        for (CompiledObject object : compiler.getObjects().values()) {
            CompiledObjectDecorator decorator = object.getDecorator();
            code.append(decorator.createCompleteSetupMethod(compiler,
                                                            object,
                                                            javaFile)
            );
        }
        String simpleBindingsCode = createInitBindingsCode(compiler);
        if (simpleBindingsCode != null && !simpleBindingsCode.isEmpty()) {
            code.append(simpleBindingsCode).append(eol);
        }

        if (compiler.getLateInitializer().length() > 0) {
            code.append("// late initializer").append(eol);
            code.append(compiler.getLateInitializer()).append(eol);
        }
        //TC-20090313 add an extra method after complete setup
        MethodDescriptor method =
                compiler.getScriptMethod(METHOD_NAME_$AFTER_COMPLETE_SETUP);

        if (method != null) {
            if (compiler.isUseHandler()) {

                compiler.reportWarning("Should not use deprecated api $afterCompleteSetup, prefer declare a 'handler' attribute on root object.");

            }
            code.append(METHOD_NAME_$AFTER_COMPLETE_SETUP + "();").append(eol);
        }
        JavaMethod javaMethod = JavaElementFactory.newMethod(PRIVATE,
                                                             TYPE_VOID,
                                                             METHOD_NAME_$COMPLETE_SETUP,
                                                             code.toString(),
                                                             false
        );
        javaFile.addMethod(javaMethod);
        return javaMethod;
    }

    protected String createInitBindingsCode(JAXXCompiler compiler) {
        String eol = JAXXCompiler.getLineSeparator();
        DataBinding[] bindings;

        StringBuilder result = new StringBuilder();

        bindings = compiler.getBindingHelper().getDataBindings();
        if (bindings.length > 0) {

            result.append(eol);
            result.append("// apply ");
            result.append(bindings.length);
            result.append(" data bindings");
            result.append(eol);
            result.append(JAXXUtil.class.getSimpleName());
            result.append("." + METHOD_NAME_APPLY_DATA_BINDING + "(this, " + FIELD_NAME_$BINDINGS + ".keySet());");
            result.append(eol);

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

                result.append(eol);
                result.append("// apply ");
                result.append(bindings.length);
                result.append(" property setters");
                result.append(eol);
                result.append(initCode.toString().trim());

            }
        }
        return result.toString();
    }

    protected JavaMethod createApplyDataBindingMethod() {
        StringBuilder buffer = new StringBuilder();
        String eol = JAXXCompiler.getLineSeparator();

        buffer.append("if (" + FIELD_NAME_ALL_COMPONENTS_CREATED + " && " + 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_ALL_COMPONENTS_CREATED + " && " + 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_ALL_COMPONENTS_CREATED + " && " + 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")
        );
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy