/*
* 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;
}
}