Package org.gwtoolbox.ioc.core.rebind

Source Code of org.gwtoolbox.ioc.core.rebind.ComponentContainerGenerator

/*
* Copyright 2002-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*       http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.gwtoolbox.ioc.core.rebind;

import com.google.gwt.core.client.GWT;
import com.google.gwt.core.ext.Generator;
import com.google.gwt.core.ext.GeneratorContext;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.*;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NodeList;
import com.google.gwt.user.client.ui.RootLayoutPanel;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;
import org.gwtoolbox.bean.rebind.BeanOracle;
import org.gwtoolbox.bean.rebind.BeanOracleBuilder;
import org.gwtoolbox.bean.rebind.JProperty;
import org.gwtoolbox.commons.conversion.client.GlobalTextConverter;
import org.gwtoolbox.commons.conversion.client.TextConverterManager;
import org.gwtoolbox.commons.generator.rebind.Criteria;
import org.gwtoolbox.commons.generator.rebind.EasyTreeLogger;
import org.gwtoolbox.commons.generator.rebind.StringUtils;
import org.gwtoolbox.ioc.core.client.*;
import org.gwtoolbox.ioc.core.client.event.ApplicationEventMulticaster;
import org.gwtoolbox.ioc.core.client.event.ApplicationEventMulticasterAware;
import org.gwtoolbox.ioc.core.client.event.DefaultApplicationEventMulticaster;
import org.gwtoolbox.ioc.core.rebind.config.*;
import org.gwtoolbox.ioc.core.rebind.processor.javaconfig.JavaConfigClassWrapper;
import org.gwtoolbox.commons.generator.rebind.LoggingUtils;

import java.io.PrintWriter;
import java.util.*;

/**
* @author Uri Boness
*/
public class ComponentContainerGenerator extends Generator {

    private final static String UNIQUE_NAME = "name";

    private static int uniqueNameCounter = 0;

    private List<String> componentIds;

    private List<String> constantNames;

    // used to collect all the ComponentTypeReferenceValues
    Map<String, ComponentTypeReferenceValue> typeNameByMethodName;

    public String generate(TreeLogger logger, GeneratorContext context, String typeName) throws UnableToCompleteException {

        componentIds = new ArrayList<String>();
        constantNames = new ArrayList<String>();
        typeNameByMethodName = new HashMap<String, ComponentTypeReferenceValue>();

        try {
            return doGenerate(new EasyTreeLogger(logger), context, typeName);
        } catch (Exception e) {
            logger.log(TreeLogger.ERROR, null, e);
            throw new UnableToCompleteException();
        } finally {
            // cleaning up
            componentIds.clear();
            constantNames.clear();
        }
    }

    public String doGenerate(EasyTreeLogger logger, GeneratorContext context, String typeName) throws Exception {

        LoggingUtils.setLogger(logger);

        try {

            TypeOracle typeOracle = context.getTypeOracle();
            JClassType type = typeOracle.getType(typeName);
            String packageName = type.getPackage().getName();

            ComponentContainerOracle oracle = new DefaultComponentContainerOracleBuilder(logger, context).build(type);

            String beanClassName = "__ContainerImpl";
            String qualifiedBeanClassName = packageName + "." + beanClassName;
            SourceWriter sourceWriter = getSourceWriter(logger, context, packageName, beanClassName, type);
            if (sourceWriter == null) {
                return qualifiedBeanClassName;
            }
            write(logger, sourceWriter, oracle, typeOracle, context);
            sourceWriter.commit(logger);
            return qualifiedBeanClassName;

        } finally {
            LoggingUtils.clearLogger();
        }
    }

    protected SourceWriter getSourceWriter(
            TreeLogger logger,
            GeneratorContext context,
            String packageName,
            String beanClassName,
            JClassType superType) {

        PrintWriter printWriter = context.tryCreate(logger, packageName, beanClassName);

        if (printWriter == null) {
            return null;
        }

        ClassSourceFileComposerFactory composerFactory = new ClassSourceFileComposerFactory(packageName, beanClassName);

        composerFactory.addImport(Map.class.getName());
        composerFactory.addImport(HashMap.class.getName());
        composerFactory.addImport(ArrayList.class.getName());
        composerFactory.addImport(List.class.getName());
        composerFactory.addImport(Set.class.getName());
        composerFactory.addImport(HashSet.class.getName());
        composerFactory.addImport(Stack.class.getName());
        composerFactory.addImport(Iterator.class.getName());
        composerFactory.addImport(TextConverterManager.class.getName());
        composerFactory.addImport(GlobalTextConverter.class.getName());
        composerFactory.addImport(Factory.class.getName());
        composerFactory.addImport(ComponentProcessor.class.getName());
        composerFactory.addImport(Disposable.class.getName());
        composerFactory.addImport(Initializable.class.getName());
        composerFactory.addImport(ApplicationEventMulticaster.class.getName());
        composerFactory.addImport(DefaultApplicationEventMulticaster.class.getName());
        composerFactory.addImport(ApplicationEventMulticasterAware.class.getName());
        composerFactory.addImport(RootPanel.class.getName());
        composerFactory.addImport(RootLayoutPanel.class.getName());
        composerFactory.addImport(Widget.class.getName());
        composerFactory.addImport(GWT.class.getName());
        composerFactory.addImport(Element.class.getName());
        composerFactory.addImport(Document.class.getName());
        composerFactory.addImport(NodeList.class.getName());

        if (superType.isInterface() != null) {
            composerFactory.addImplementedInterface(superType.getQualifiedSourceName());
        } else {
            composerFactory.setSuperclass(superType.getQualifiedSourceName());
        }

        return composerFactory.createSourceWriter(context, printWriter);
    }

    protected void write(EasyTreeLogger logger, SourceWriter writer, ComponentContainerOracle oracle, TypeOracle typeOracle, GeneratorContext context)
            throws UnableToCompleteException {

        writeFields(writer, oracle);
        writeMethods(logger, writer, oracle, typeOracle, context);
        writeStaticInitialization(logger, writer, oracle, typeOracle);
        writeInnerClasses(logger, writer, oracle, typeOracle);
    }

    protected void writeFields(SourceWriter writer, ComponentContainerOracle oracle) {
        writer.println("private final static Map<String, ComponentSource> componentSourceById = new HashMap<String, ComponentSource>();");
        writer.println("private final static GlobalTextConverter converter = TextConverterManager.getGlobalConverter();");
        writer.println("private final static Stack<Disposable> disposables = new Stack<Disposable>();");
        writer.println("private final static List<ComponentProcessor> componentProcessors = new ArrayList<ComponentProcessor>();");
        writer.println("private static List<EagerInitializer> initializers = new ArrayList<EagerInitializer>();");
        writer.println("private static __ContainerImpl container = new __ContainerImpl();");
    }

    protected void writeMethods(EasyTreeLogger logger, SourceWriter writer, ComponentContainerOracle oracle, TypeOracle typeOracle, GeneratorContext context)
            throws UnableToCompleteException {

        writeComponentContainerMethods(writer, oracle, typeOracle);
        writeInitializationMethods(writer);
        writeJavaConfigMethods(logger, writer, typeOracle, oracle, context);
        writeComponentDefinitions(logger, writer, typeOracle, oracle, context);
        writeConstantDefinitions(logger, writer, typeOracle, oracle, context);
        writeAutowiredTypesFinderMethods(logger, writer, oracle, typeOracle);
    }

    protected void writeComponentContainerMethods(SourceWriter writer, ComponentContainerOracle oracle, TypeOracle typeOracle) {

        writer.println("public static Object __getComponent(String id) {");
        writer.println("    ComponentSource source = (ComponentSource)componentSourceById.get(id);");
        writer.println("    return (source == null) ? null : source.getComponent();");
        writer.println("}");

        writer.println("public Object getComponent(String id) {");
        writer.println("    return __getComponent(id);");
        writer.println("}");

        writer.println();

        writer.println("public static boolean __containsComponent(String id) {");
        writer.println("    return componentSourceById.containsKey(id);");
        writer.println("}");

        writer.println("public boolean containsComponent(String id) {");
        writer.println("    return __containsComponent(id);");
        writer.println("}");

        writer.println();

        writer.println("public ApplicationEventMulticaster getApplicationEventMulticaster() {");
        writer.println("    return __getApplicationEventMulticaster();");
        writer.println("}");

        writer.println();

        writeMulticasterMethod(writer, oracle, typeOracle);

        writer.println();

        writeMetaPropertyResolveMethod(writer, oracle, typeOracle);

        writer.println();

        writer.println("public void close() {");
        writer.println("    while (!disposables.empty()) {");
        writer.println("        try {");
        writer.println("            ((Disposable)disposables.pop()).dispose();");
        writer.println("        } catch (Exception e) {  }"); // should we do something with the exception???
        writer.println("    }");
        writer.println("}");

    }

    protected void writeMulticasterMethod(SourceWriter writer, ComponentContainerOracle oracle, TypeOracle typeOracle) {
        writer.println("public static ApplicationEventMulticaster __getApplicationEventMulticaster() {");
        writer.println("    return (ApplicationEventMulticaster) __getComponent(\"" + oracle.getMulticasterComponentId() + "\");");
        writer.println("}");
    }

    protected void writeMetaPropertyResolveMethod(SourceWriter writer, ComponentContainerOracle oracle, TypeOracle typeOracle) {

        writer.println("private final static java.util.Map<String, String> META_PROPERTIES = new java.util.HashMap<String, String>();");
        writer.println("static {");
        writer.println("    NodeList<Element> nodes = Document.get().getDocumentElement().getElementsByTagName(\"META\");");
        writer.println("    for (int i = 0; i < nodes.getLength(); i++) {");
        writer.println("        Element element = nodes.getItem(i);");
        writer.println("        String name = element.getAttribute(\"name\");");
        writer.println("        if (!\"gwtoolbox:property\".equals(name)) {");
        writer.println("            continue;");
        writer.println("        }");
        writer.println("        String value = element.getAttribute(\"content\");");
        writer.println("        if (value.indexOf('=') < 0) {");
        writer.println("            throw new RuntimeException(\"Invalid gwtoolbox meta property value '\" + value + \"'\");");
        writer.println("        }");
        writer.println("        String[] nameValue = value.split(\"=\");");
        writer.println("        name = nameValue[0];");
        writer.println("        value = nameValue[1];");
        writer.println("        META_PROPERTIES.put(name, value);");
        writer.println("    }");
        writer.println("}");

        writer.println("public static String __resolveMetaProperty(String name) {");
        writer.println("    return META_PROPERTIES.get(name);");
        writer.println("}");
    }

    protected void writeInitializationMethods(SourceWriter writer) {

        writer.println("protected static <T> T beforeInitialization(T component, String componentId) {");
        writer.println("    for (ComponentProcessor processor : componentProcessors) {");
        writer.println("        component = processor.processBeforeInitialization(component, componentId);");
        writer.println("    }");
        writer.println("    return component;");
        writer.println("}");

        writer.println();

        writer.println("protected static <T> T afterInitialization(T component, String componentId) {");
        writer.println("    for (ComponentProcessor processor : componentProcessors) {");
        writer.println("        component = processor.processAfterInitialization(component, componentId);");
        writer.println("    }");
        writer.println("    return component;");
        writer.println("}");

        writer.println();
    }

    protected void writeJavaConfigMethods(
            EasyTreeLogger logger,
            SourceWriter writer,
            TypeOracle typeOracle,
            ComponentContainerOracle oracle,
            GeneratorContext context) throws UnableToCompleteException {

        JavaConfigClassWrapper wrapper = oracle.getJavaConfigClassWrapper();
        if (wrapper == null) {
            return;
        }
        List<JavaConfigConfiguredProperty> configuredProperties = wrapper.getConfiguredProperties();
        for (JavaConfigConfiguredProperty configuredProperty : configuredProperties) {
            JMethod method = configuredProperty.getMethod();
            String propertyName = configuredProperty.getPropertyName();
            PropertyReferenceValue value = configuredProperty.isMeta() ?
                    new RuntimePropertyReferenceValue(propertyName) : new PropertyReferenceValue(propertyName);
            String methodName = method.getName();
            JType returnType = method.getReturnType();
            String visibility = method.isPublic() ? "public " : method.isDefaultAccess() ? "" : "protected ";
            writer.println(visibility + returnType.getQualifiedSourceName() + " " + methodName + "() {");
            String valueCode = generateValueCode(value, logger, writer, oracle, typeOracle, returnType, new HashSet<ComponentDefinition>());
            writer.println("    return " + valueCode + ";");
            writer.println("}");
        }

    }

    protected void writeConstantDefinitions(
            EasyTreeLogger logger,
            SourceWriter writer,
            TypeOracle typeOracle,
            ComponentContainerOracle oracle,
            GeneratorContext context) throws UnableToCompleteException {

        for (ConstantDefinition definition : oracle.getConstantDefinitions()) {
            writeConstantDefinitionMethods(writer, definition);
        }
    }

    protected void writeConstantDefinitionMethods(SourceWriter writer, ConstantDefinition definition) throws UnableToCompleteException {
        writer.println("protected static Object get_" + definition.getId() + "() {");
        writer.println("    return " + definition.getFieldName() + ";");
        writer.println("}");
    }

    protected void writeComponentDefinitions(
            EasyTreeLogger logger,
            SourceWriter writer,
            TypeOracle typeOracle,
            ComponentContainerOracle oracle,
            GeneratorContext context) throws UnableToCompleteException {

        for (ComponentDefinition definition : oracle.getComponentDefinitions()) {
            writeComponentDefinitionMethods(logger, writer, typeOracle, oracle, context, definition);
        }
    }

    protected void writeComponentDefinitionMethods(
            EasyTreeLogger logger,
            SourceWriter writer,
            TypeOracle typeOracle,
            ComponentContainerOracle oracle,
            GeneratorContext context,
            ComponentDefinition definition) throws UnableToCompleteException {

        if (JavaConfigComponentDefinition.class.isInstance(definition)) {
            writeJavaConfigComponentDefinitionMethods(logger, writer, oracle, (JavaConfigComponentDefinition) definition,
                    oracle.getComponentContainerType(), typeOracle);
        } else {
            writeSimpleComponentDefinitionMethods(logger, writer, typeOracle, oracle, context, (SimpleComponentDefinition) definition);
        }
    }

    protected void writeJavaConfigComponentDefinitionMethods(
            EasyTreeLogger logger,
            SourceWriter writer,
            ComponentContainerOracle oracle,
            JavaConfigComponentDefinition definition,
            JClassType containerType,
            TypeOracle typeOracle) throws UnableToCompleteException {

        String jcMethodName = uniquName("jc__");

        writer.println();
        writer.println();
        writer.beginJavaDocComment();
        writer.println("JavaConfig Component: '" + definition.getId() + "'");
        writer.endJavaDocComment();
        writer.println();

        writer.println("protected static Object get_" + definition.getId() + "() {");
        writer.println("    return container." + jcMethodName + "();");
        writer.println("}");

        JClassType componentType = definition.getComponentType();
        String componentId = definition.getId();

        boolean factoryComponent = isFactory(componentType, typeOracle);
        if (!definition.isSingleton() && factoryComponent) {
            logger.error("JavaConfig Component '" + componentId + "' is a prototype factory bean. Factory " +
                    "beans need to be defined as singletons");
            throw new UnableToCompleteException();
        }

        String componentTypeName = componentType.getQualifiedSourceName();
        if (definition.isSingleton()) {
            writer.println("private static " + componentTypeName + " " + componentId + ";");
        }

        JMethod method = definition.getMethod();

        writer.println("Object " + jcMethodName + "() {");
        writer.indent();

        if (definition.isSingleton()) {
            writer.println("if (" + componentId + " != null) {");
            if (factoryComponent) {
                writer.println("    return " + componentId + ".getObject();");
            } else {
                writer.println("    return " + componentId + ";");
            }
            writer.println("}");
        }

        StringBuilder paramsBuilder = new StringBuilder();
        for (ParameterSetting paramSetting : definition.getParameterSettings()) {
            if (paramsBuilder.length() != 0) {
                paramsBuilder.append(", ");
            }
            JParameter parameter = paramSetting.getParameter();
            Value value = paramSetting.getValue();
            String valueString = generateValueCode(value, logger, writer, oracle, typeOracle, parameter.getType(), Collections.EMPTY_SET);
            paramsBuilder.append("((" + parameter.getType().getQualifiedSourceName() + ") " + valueString + ")");
        }

        writer.println(componentTypeName + " component = super." + method.getName() + "(" + paramsBuilder.toString() + ");");

        writer.println("if (component instanceof ApplicationEventMulticasterAware) {");
        writer.println("    ((ApplicationEventMulticasterAware) component).setApplicationEventMulticaster(__getApplicationEventMulticaster());");
        writer.println("}");

        // calling all the ComponentPostProcessor's before the bean is initialized.
        writer.println("component = beforeInitialization(component, \"" + componentId + "\");");

        // Support for Initializable callbacks
        if (componentType.isAssignableTo(typeOracle.findType(Initializable.class.getName()))) {
            writer.println("try {");
            writer.println("    component.init();");
            writer.println("} catch (Exception e) { throw new RuntimeException(e); }");
        }
        String initMethodName = definition.getInitMethodName();
        if (StringUtils.hasLength(initMethodName)) {
            writer.println("try {");
            writer.println("    component." + initMethodName + "();");
            writer.println("} catch (Exception e) { throw new RuntimeException(e); }");
        }

        // calling all the ComponentProcessor's after the bean was initialized.
        writer.println("component = afterInitialization(component, \"" + componentId + "\");");

        // Support for Disposable callbacks
        if (componentType.isAssignableTo(typeOracle.findType(Disposable.class.getName()))) {
            writer.println("disposables.push(component);");
        }

        String disposeMethodName = definition.getDisposeMethodName();
        if (StringUtils.hasLength(disposeMethodName)) {
            writer.println("final " + componentTypeName + " tempComponent = component;");
            writer.println("disposables.push(new Disposable() {");
            writer.println("    public void dispose() throws Exception {");
            writer.println("        tempComponent." + disposeMethodName + "();");
            writer.println("    }");
            writer.println("});");
        }

        if (definition.isSingleton()) {
            writer.println(componentId + " = component;");
        }

        if (factoryComponent) {
            writer.println("return component.getObject();");
        } else {
            writer.println("return component;");
        }

        writer.println("}");

        writer.println("static {");
        writer.println();
        writer.indent();

        // Registering the component source for this component definition. The component source will be used by the
        // ComponentContainer methods.
        if (definition.isPubliclyAccessible()) {
            writer.println("componentSourceById.put(\"" + componentId + "\", new ComponentSource() {");
            writer.println("    public Object getComponent() { return get_" + componentId + "(); }");
            writer.println("});");
            writer.println();
        }

        if (isComponentProcessor(componentType, typeOracle)) {
            writer.println("beanPostProcessors.add(get_" + componentId + "());");
        }

        // If the component should be initialized eagerly (that is, on construction time) then calling the appropriate
        // "get_" method once just to initialize it (only if it's a singleton... with prototypes it makes no sense).
        else if (!definition.isLazy() && definition.isSingleton()) {
            writer.println("initializers.add(new EagerInitializer() {");
            writer.println("    public void init() { get_" + componentId + "(); }");
            writer.println("});");
        }

        writer.outdent();
        writer.println();
        writer.println("}");
    }

    protected void writeSimpleComponentDefinitionMethods(
            EasyTreeLogger logger,
            SourceWriter writer,
            TypeOracle typeOracle,
            ComponentContainerOracle oracle,
            GeneratorContext context,
            SimpleComponentDefinition definition) throws UnableToCompleteException {

        String componentId = escapeComponentId(definition.getId());
        componentIds.add(componentId);

        JClassType componentType = definition.isGenerated() ? definition.getGeneratedType() : definition.getComponentType();
        String componentClassName = componentType.getQualifiedSourceName();

        BeanOracle beanOracle = new BeanOracleBuilder(typeOracle).build(logger, componentType);

        Set<ComponentDefinition> nestedComponentDefinitions = new HashSet<ComponentDefinition>();

        boolean factoryComponent = isFactory(componentType, typeOracle);

        // first we check that if the component is a factory component and if it is also configured as a singleton. We
        // only support singleton factory components as it makes no sense to have proptotype ones.
        if (factoryComponent && !definition.isSingleton()) {
            logger.error("FactoryComponent '" + componentId + "' is not a singleton. It makes no sense " +
                    "to create a proptotype factory component!!!");
            throw new UnableToCompleteException();
        }

        writer.println();
        writer.println();
        writer.beginJavaDocComment();
        writer.println("Component: '" + componentId + "'");
        writer.endJavaDocComment();
        writer.println();

        // if the component is a singleton, we declare a static class variable to hold the single instance of the bean.
        if (definition.isSingleton()) {
            writer.println("private static " + componentClassName + " " + componentId + ";");
        }

        writer.println();

        writer.println("protected static Object get_" + componentId + "() {");
        writer.indent();

        // if the bean definition is a singleton
        if (definition.isSingleton()) {
            if (factoryComponent) {
                writer.println("if (" + componentId + " != null) { return " + componentId + ".getObject();}");
            } else {
                writer.println("if (" + componentId + " != null) { return " + componentId + "; }");
            }
        }

        ConstructorInjectionDefinition ctorDef = definition.getConstructorInjectionDefinition();
        if (ctorDef == null) {
            if (definition.isGenerated()) {
                writer.println(componentClassName + " component = (" + componentClassName + ") GWT.create(" + definition.getComponentType().getQualifiedSourceName() + ".class);");
            } else {
                writer.println(componentClassName + " component = new " + componentClassName + "();");
            }
        } else {

            StringBuilder paramsBuilder = new StringBuilder();
            for (ParameterSetting paramSetting : ctorDef.getParameterSettings()) {
                if (paramsBuilder.length() != 0) {
                    paramsBuilder.append(", ");
                }
                JParameter parameter = paramSetting.getParameter();
                Value value = paramSetting.getValue();
                String valueString = generateValueCode(value, logger, writer, oracle, typeOracle, parameter.getType(), Collections.EMPTY_SET);
                paramsBuilder.append("((" + parameter.getType().getQualifiedSourceName() + ") " + valueString + ")");
            }
            writer.println(componentClassName + " component = new " + componentClassName + "(" + paramsBuilder.toString() + ");");
        }

        // if it's a singleton then initializing the static class variable. It's important to set the static
        // instance now to support cyclic dependencies with singletons.
        if (definition.isSingleton()) {
            writer.println(componentId + " = component;");
        }

        for (PropertySetting setting : definition.getPropertySettings()) {
            String propertyName = setting.getPropertyName();
            if (logger.debugEnabled()) {
                logger.debug("Writing property wiring code for '" + propertyName + "' of '" +
                        definition.getComponentType().getQualifiedSourceName() + "' bean class");
            }
            JProperty property = beanOracle.getProperty(propertyName);
            if (property == null || !property.isMutable()) {
                logger.error("Cannot set property '" + propertyName + "' on component '" +
                        componentClassName + "' in container. The property is missing or is read only");
                throw new UnableToCompleteException();
            }

            Value value = setting.getValue();
            String valueString = generateValueCode(value, logger, writer, oracle, typeOracle, property.getType(), nestedComponentDefinitions);
            writer.println("component.set" + StringUtils.capitalize(property.getName()) + "(" + cast(valueString, property.getSetter().getParameters()[0].getType()) + ");");
        }

        writer.println("if (component instanceof ApplicationEventMulticasterAware) {");
        writer.println("    ((ApplicationEventMulticasterAware) component).setApplicationEventMulticaster(__getApplicationEventMulticaster());");
        writer.println("}");

        Set<JMethod> handlers = definition.getEventHandlers();
        if (!handlers.isEmpty()) {
            String handlerClassName = EventHandlerListenerClassGenerator.generate(logger.branchInfo("Generating event handler"), componentType, handlers, context);
            if (handlerClassName != null) {
                writer.println("__getApplicationEventMulticaster().registerListener(new " + handlerClassName + "(component));");
            }
        }

        if (componentType.isAssignableTo(typeOracle.findType(ComponentContainerAware.class.getName()))) {
            writer.println("component.setComponentContainer(container);");
        }

        // calling all the ComponentPostProcessor's before the bean is initialized.
        writer.println("component = beforeInitialization(component, \"" + componentId + "\");");

        // Support for Initializable callbacks
        if (componentType.isAssignableTo(typeOracle.findType(Initializable.class.getName()))) {
            writer.println("try {");
            writer.println("    component.init();");
            writer.println("} catch (Exception e) { throw new RuntimeException(e); }");
        }

        String initMethodName = definition.getInitMethodName();
        if (StringUtils.hasLength(initMethodName)) {
            writer.println("try {");
            writer.println("    component." + initMethodName + "();");
            writer.println("} catch (Exception e) { throw new RuntimeException(e); }");
        }

        // calling all the ComponentProcessor's after the bean was initialized.
        writer.println("component = afterInitialization(component, \"" + componentId + "\");");

        // Support for Disposable callbacks
        if (componentType.isAssignableTo(typeOracle.findType(Disposable.class.getName()))) {
            writer.println("disposables.push(component);");
        }

        String disposeMethodName = definition.getDisposeMethodName();
        if (StringUtils.hasLength(disposeMethodName)) {
            writer.println("final " + componentClassName + " tempComponent = component;");
            writer.println("disposables.push(new Disposable() {");
            writer.println("    public void dispose() throws Exception {");
            writer.println("        tempComponent." + disposeMethodName + "();");
            writer.println("    }");
            writer.println("});");
        }

        if (factoryComponent) {
            writer.println("return component.getObject();");
        } else {
            writer.println("return component;");
        }

        writer.outdent();
        writer.println("}");

        writer.println();

        writer.println("static {");
        writer.println();
        writer.indent();

        // Registering the component source for this component definition. The component source will be used by the
        // ComponentContainer methods.
        if (definition.isPubliclyAccessible()) {
            writer.println("componentSourceById.put(\"" + componentId + "\", new ComponentSource() {");
            writer.println("    public Object getComponent() { return get_" + componentId + "(); }");
            writer.println("});");
            writer.println();
        }

        if (isComponentProcessor(componentType, typeOracle)) {
            writer.println("beanPostProcessors.add(get_" + componentId + "());");
        }

        // If the component should be initialized eagerly (that is, on construction time) then calling the appropriate
        // "get_" method once just to initialize it (only if it's a singleton... with prototypes it makes no sense).
        else if (!definition.isLazy() && definition.isSingleton()) {
            writer.println("initializers.add(new EagerInitializer() {");
            writer.println("    public void init() { get_" + componentId + "(); }");
            writer.println("});");
        }

        writer.outdent();
        writer.println();
        writer.println("}");

        // writing all the internal (nested) bean definition methods.
        for (ComponentDefinition nestedDefinition : nestedComponentDefinitions) {
            writeComponentDefinitionMethods(logger, writer, typeOracle, oracle, context, nestedDefinition);
        }
    }

    @SuppressWarnings("ConstantConditions")
    protected String generateValueCode(
            Value value,
            EasyTreeLogger logger,
            SourceWriter writer,
            ComponentContainerOracle oracle,
            TypeOracle typeOracle,
            JType expectedType,
            Set<ComponentDefinition> componentDefinitionRegistry) throws UnableToCompleteException {

        // handling runtime property values
        if (RuntimePropertyReferenceValue.class.isInstance(value)) {
            String propertyName = ((RuntimePropertyReferenceValue) value).getValue();
            JClassType stringType = typeOracle.findType("java.lang.String");
            JType type = expectedType;
            if (type == null) {
                type = stringType;
            }
            JClassType classType = type.isClassOrInterface();
            if (classType != null && classType.isAssignableTo(stringType)) {
                return "__resolveMetaProperty(\"" + propertyName + "\")";
            }
            return "converter.toValue(" + type.getQualifiedSourceName() + ".class, __resolveMetaProperty(\"" + propertyName + "\"))";
        }

        // handling values which are defined as property references.
        if (PropertyReferenceValue.class.isInstance(value)) {
            String propertyName = ((PropertyReferenceValue) value).getValue();
            String defaultValue = ((PropertyReferenceValue) value).getDefaultValue();
            String propertyValue = oracle.getPropertyResolver().resolve(propertyName);
            if (propertyValue == null) {
                propertyValue = defaultValue;
            }
            if (propertyValue == null) {
                logger.error("Could not resolve property '" + propertyName + "' from resource bundle");
                throw new UnableToCompleteException();
            }
            value = new TypedStringValue(new TypedString(propertyValue));
        }

        // handling values which are defined as strings but represent other types.
        if (TypedStringValue.class.isInstance(value)) {
            TypedString typedString = ((TypedStringValue) value).getValue();
            JClassType stringType = typeOracle.findType("java.lang.String");
            JType type = typedString.getType();
            if (type == null) {
                type = expectedType;
            }
            if (type == null) {
                type = stringType;
            }
            JClassType classType = type.isClassOrInterface();
            if (classType != null && classType.isAssignableTo(stringType)) {
                return "\"" + typedString.getValue() + "\"";
            }
            return "converter.toValue(" + type.getQualifiedSourceName() + ".class, \"" + typedString.getValue() + "\")";
        }

        // see comments below when we check ComponentIdReferenceValue.
        if (ConstantIdReferenceValue.class.isInstance(value)) {
            IdRererence ref = ((ConstantIdReferenceValue) value).getValue();
            ConstantDefinition cd = oracle.getConstantDefinition(ref.getId());
            return cd.getFieldName();
        }

        // handling references to other bean definitions. Currently we support component references that reference
        // constants. We should consider remove the distinction between constants and components all together (in terms
        // of references... that is, have only one IdReferenceValue instead of two)
        if (ComponentIdReferenceValue.class.isInstance(value)) {
            IdRererence ref = ((ComponentIdReferenceValue) value).getValue();
            String refId = ref.getId();
            if (oracle.hasConstantDefinition(refId)) {
                ConstantDefinition cd = oracle.getConstantDefinition(refId);
                return cd.getFieldName();
            }
            return "get_" + refId + "()";
        }

        if (ComponentTypeReferenceValue.class.isInstance(value)) {
            JClassType type = ((ComponentTypeReferenceValue) value).getValue().getType();
            String methodName = "findByType_" + (typeNameByMethodName.size() + 1);
            if (logger.debugEnabled()) {
                logger.debug("Defining method '" + methodName + "'");
            }
            typeNameByMethodName.put(methodName, ((ComponentTypeReferenceValue) value));
            return methodName + "()";
        }

        // handling inner component definitions. These component definition are placed within other component definitions.
        if (ComponentDefinitionValue.class.isInstance(value)) {
            ComponentDefinition componentDefinition = ((ComponentDefinitionValue) value).getValue();
            componentDefinitionRegistry.add(componentDefinition);
            return "__getComponent(\"" + componentDefinition.getId() + "\")";
        }

        // handling list and set values. Spring loads these as ManagedList and ManagedSet values.
        if (CollectionValue.class.isInstance(value)) {
            String colName = uniqueName();
            if (ListValue.class.isInstance(value)) {
                writer.println("List " + colName + " = new ArrayList(" + ((ListValue) value).getValue().size() + ");");
            } else {
                writer.println("Set " + colName + " = new HashSet(" + ((SetValue) value).getValue().size() + ");");
            }
            for (Value valueElement : ((CollectionValue) value).getValue()) {
                String valueString = generateValueCode(valueElement, logger, writer, oracle, typeOracle, null, componentDefinitionRegistry);
                writer.println(colName + ".add(" + valueString + ");");
            }
            return colName;
        }

        // handling map values. Spring loads these as ManagedMap and ManagedSet values.
        if (MapValue.class.isInstance(value)) {
            String mapName = uniqueName();
            writer.println("Map " + mapName + " = new HashMap();");
            Map<Value, Value> map = ((MapValue) value).getValue();
            for (Map.Entry<Value, Value> entry : map.entrySet()) {
                Value keyValue = entry.getKey();
                String keyValueString = generateValueCode(keyValue, logger, writer, oracle, typeOracle, null, componentDefinitionRegistry);
                Value valueValue = entry.getValue();
                String valueValueString = generateValueCode(valueValue, logger, writer, oracle, typeOracle, null, componentDefinitionRegistry);
                writer.println(mapName + ".put(" + keyValueString + ", " + valueValueString + ");");
            }
            return mapName;
        }

        // handling normal values. These values are text based and should rely on the TextConverter mechanism (in
        // gwtoolbox-common) to conver the text values to the real values. Spring loads these as TypedStringValue values.
        if (SimpleValue.class.isInstance(value)) {
            Object realValue = value.getValue();
            if (String.class.isInstance(realValue)) {
                JClassType stringType = typeOracle.findType("java.lang.String");
                if (expectedType.isClass() != null && expectedType.isClass().isAssignableTo(stringType)) {
                    return "\"" + realValue + "\"";
                }
                return "converter.toValue(" + expectedType.getQualifiedSourceName() + ".class, \"" + realValue + "\")";
            }
            if (Character.class.isInstance(value)) {
                return "'" + realValue + "'";
            }
            return String.valueOf(realValue);
        }

        logger.error("ComponentContainerGenerator does not support property value " +
                "definitions of type '" + value.getClass().getName() + "'");
        throw new UnableToCompleteException();
    }

    protected void writeAutowiredTypesFinderMethods(EasyTreeLogger logger, SourceWriter writer, ComponentContainerOracle oracle, TypeOracle typeOracle) throws UnableToCompleteException {
        for (Map.Entry<String, ComponentTypeReferenceValue> entry : typeNameByMethodName.entrySet()) {
            String methodName = entry.getKey();
            logger.debug("method name: '" + methodName + "'");
            JClassType type = entry.getValue().getValue().getType().isClassOrInterface();
            Criteria<JClassType> criteria = entry.getValue().getCriteria();
            if (type == null) {
                logger.error("Unable to autowire/inject primitives by type");
                throw new UnableToCompleteException();
            }

            // first checking if the autowired type is a collection or an array. if so, trying to find all components
            // in the container that match the component/element type of the array/collection. Then, building the
            // appropriate collection/array with them and injecting it.

            JClassType collectionType = typeOracle.findType("java.util.Collection");
            if (type.isAssignableTo(collectionType)) {
                writeCollectionAutowiredTypeFinderMethod(logger, writer, oracle, methodName, type, criteria);
                continue;
            }

            JArrayType arrayType = type.isArray();
            if (arrayType != null) {
                writeArrayAutowiredTypeFinderMethod(logger, writer, oracle, methodName, type, criteria);
                continue;
            }

            writeSimpleAutowiredTypeFinderMethod(logger, writer, oracle, methodName, type, criteria);
        }
    }

    protected void writeCollectionAutowiredTypeFinderMethod(EasyTreeLogger logger, SourceWriter writer, ComponentContainerOracle oracle, String methodName, JClassType type, Criteria<JClassType> criteria)
        throws UnableToCompleteException {

        logger.debug("writing method name (collection): '" + methodName + "'");

        writer.println("protected static " + type.getQualifiedSourceName() + " " + methodName + "() {");

        JClassType collectionType = type.getOracle().findType("java.util.List");
        if (type.isAssignableTo(collectionType)) {
            writer.println("List collection = new ArrayList();");
        } else {
            collectionType = type.getOracle().findType("java.util.Set");
            if (type.isAssignableTo(collectionType)) {
                writer.println("Set collection = new HashSet();");
            } else {
                logger.error("Found collection of type '" + type.getQualifiedSourceName() + "'. Cannot autowire a " +
                        "collection which is not a java.util.List or java.util.Set");
                throw new UnableToCompleteException();
            }
        }

        JClassType[] typeArgs = type.asParameterizationOf(collectionType.isGenericType()).getTypeArgs();
        JClassType elementType = typeArgs[0];

        for (ComponentDefinition definition : oracle.getComponentDefinitions(elementType)) {
            if (!definition.isPubliclyAccessible() || !criteria.check(definition.getComponentType())) {
                continue;
            }
            writer.println("collection.add((" + elementType.getQualifiedSourceName() + ") get_" + definition.getId() + "());");
        }

        writer.println("    return collection;");
        writer.println("}");

    }

    protected void writeArrayAutowiredTypeFinderMethod(EasyTreeLogger logger, SourceWriter writer, ComponentContainerOracle oracle, String methodName, JClassType type, final Criteria<JClassType> criteria)
        throws UnableToCompleteException {

        logger.debug("writing method name (array): '" + methodName + "'");

        final JClassType elementType = type.isArray().getComponentType().isClassOrInterface();
        if (elementType == null) {
            logger.error("Cannot autowire primitive arrays by type");
            throw new UnableToCompleteException();
        }

        writer.println("protected static " + elementType.getQualifiedSourceName() + "[] " + methodName + "() {");

        Collection<ComponentDefinition> definitions = oracle.getComponentDefinitions(new Criteria<ComponentDefinition>() {
            public boolean check(ComponentDefinition definition) {
                return definition.isPubliclyAccessible()
                        && definition.getComponentType().isAssignableTo(elementType)
                        && criteria.check(definition.getComponentType());
            }
        });

        writer.println(elementType.getQualifiedSourceName() + "[] array = new " + elementType.getQualifiedSourceName() + "[" + definitions.size() + "];");
        int i = 0;
        for (ComponentDefinition definition : definitions) {
            writer.println("array[" + i + "] = get_" + definition.getId() + "();");
            i++;
        }

        writer.println("    return array;");
        writer.println("}");
    }

    protected void writeSimpleAutowiredTypeFinderMethod(EasyTreeLogger logger, SourceWriter writer, ComponentContainerOracle oracle, String methodName, final JClassType type, final Criteria<JClassType> criteria)
            throws UnableToCompleteException {

        logger.debug("writing method name: '" + methodName + "'");

        String typeName = type.getQualifiedSourceName();
        String bestCandidateComponentId = null;
//        boolean multiple = false;
        Collection<ComponentDefinition> definitions = oracle.getComponentDefinitions(new Criteria<ComponentDefinition>() {
            public boolean check(ComponentDefinition definition) {
                if (!definition.isPubliclyAccessible() || !criteria.check(definition.getComponentType())) {
                    return false;
                }
                JClassType componentType = definition.isGenerated() ? definition.getGeneratedType() : definition.getComponentType();
                if (!componentType.isAssignableTo(type)) {
                    return false;
                }
                return true;
            }
        });
        for (ComponentDefinition definition : definitions) {
            JClassType componentType = definition.isGenerated() ? definition.getGeneratedType() : definition.getComponentType();
            if (componentType.equals(type)) {
                bestCandidateComponentId = definition.getId();
                break;
            }
        }
        if (bestCandidateComponentId == null && !definitions.isEmpty()) {
            bestCandidateComponentId = definitions.iterator().next().getId();
        }
//
//        for (ComponentDefinition definition : oracle.getComponentDefinitions()) {
//            if (!definition.isPubliclyAccessible() || !criteria.check(definition.getComponentType())) {
//                continue;
//            }
//
//            // if the type is generated, the actual type that is registered in the container is the generated one
//            JClassType componentType = definition.isGenerated() ? definition.getGeneratedType() : definition.getComponentType();
//            if (componentType.equals(type)) {
//                bestCandidateComponentId = definition.getId();
//                break;
//            }
//
//            if (componentType.isAssignableTo(type)) {
//                if (bestCandidateComponentId != null) {
//                    multiple = true;
//                }
//                bestCandidateComponentId = definition.getId();
//            }
//        }
//        if (multiple) {
//            logger.error("There are multiple beans that are assignable to '" + typeName + "' therefore it cannot be autowired");
//            throw new UnableToCompleteException();
//        }

        if (bestCandidateComponentId == null) {
            logger.error("Cannot autowire! Could not find a bean which is assignable to '" + typeName + "'");
            throw new UnableToCompleteException();
        }

        writer.println("protected static " + typeName + " " + methodName + "() {");
        writer.println("    return (" + typeName + ") get_" + bestCandidateComponentId + "();");
        writer.println("}");
    }

    protected void writeStaticInitialization(EasyTreeLogger logger, SourceWriter writer, ComponentContainerOracle oracle, TypeOracle typeOracle) {

        writer.println();
        writer.println();
        writer.beginJavaDocComment();
        writer.println("Static Initialization");
        writer.endJavaDocComment();
        writer.println();

        writer.println("static {");
        writer.println();

        // executing eager initialization of components
        writer.println("    for (EagerInitializer initializer : initializers) {");
        writer.println("        initializer.init();");
        writer.println("    }");
        writer.println("    initializers.clear(); initializers = null;");
        writer.println();
        writer.println();

        // binding components to the root panel
        for (RootPanelBindingDefinition binding : oracle.getRootPanelBindingDefinitions()) {
            String rootId = binding.getRootId();
            String componentId = binding.getComponentId();
            if (rootId == null) {
                String rootPanelVariable = binding.isUseRootLayoutPanel() ? "RootLayoutPanel" : "RootPanel";
                writer.println("    " + rootPanelVariable + ".get().add((Widget)__getComponent(\"" + componentId + "\"));");
            } else {
                writer.println("    RootPanel.get(\"" + rootId + "\").add((Widget)__getComponent(\"" + componentId + "\"));");
            }
        }

        writer.println();
        writer.println("}");
    }

    protected void writeInnerClasses(TreeLogger logger, SourceWriter writer, ComponentContainerOracle oracle, TypeOracle typeOracle) {
        writer.beginJavaDocComment();
        writer.println("Inner Classes");
        writer.endJavaDocComment();
        writer.println();
        writeComponentSourceInterface(writer);
        writer.println();
        writeEagerInitializerInterface(writer);
    }

    protected void writeComponentSourceInterface(SourceWriter writer) {
        writer.println("private static interface ComponentSource {");
        writer.println("    Object getComponent();");
        writer.println("}");
    }

    protected void writeEagerInitializerInterface(SourceWriter writer) {
        writer.println("private static interface EagerInitializer {");
        writer.println("    void init();");
        writer.println("}");
    }

    //============================================== Helper Methods ====================================================

    //todo cover all the illegal characters for methods according to the Java spec.

    private String escapeComponentId(String id) {
        return id;
    }

    private boolean isFactory(JClassType componentType, TypeOracle typeOracle) {
        return componentType.isAssignableTo(typeOracle.findType(Factory.class.getName()));
    }

    private boolean isComponentProcessor(JClassType beanType, TypeOracle typeOracle) {
        return beanType.isAssignableTo(typeOracle.findType(ComponentProcessor.class.getName()));
    }

    private String uniquName(String prefix) {
        return prefix + uniqueNameCounter++;
    }

    private String uniqueName() {
        return uniquName(UNIQUE_NAME);
    }

    private String cast(String value, JType toType) {
        JPrimitiveType primitiveType = toType.isPrimitive();
        if (primitiveType == null) {
            return "(" + toType.getQualifiedSourceName() + ")" + value;
        }
        return value;
    }

}
TOP

Related Classes of org.gwtoolbox.ioc.core.rebind.ComponentContainerGenerator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.