/* * Copyright (C) 2012 Red Hat, Inc. and/or its affiliates. * * 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.jboss.errai.ui.rebind; import static org.jboss.errai.codegen.util.Stmt.loadLiteral; import java.util.LinkedHashMap; import java.util.Map; import com.google.gwt.user.client.ui.IsWidget; import org.jboss.errai.codegen.Statement; import org.jboss.errai.codegen.exception.GenerationException; import org.jboss.errai.codegen.meta.MetaClass; import org.jboss.errai.codegen.util.Stmt; import org.jboss.errai.common.client.api.IsElement; import org.jboss.errai.common.client.ui.ElementWrapperWidget; import org.jboss.errai.common.client.ui.HasValue; import org.jboss.errai.ioc.client.api.CodeDecorator; import org.jboss.errai.ioc.rebind.ioc.extension.IOCDecoratorExtension; import org.jboss.errai.ioc.rebind.ioc.injector.api.Decorable; import org.jboss.errai.ioc.rebind.ioc.injector.api.FactoryController; import org.jboss.errai.ioc.rebind.ioc.injector.api.InjectionContext; import org.jboss.errai.ui.shared.Template; import org.jboss.errai.ui.shared.TemplateUtil; import org.jboss.errai.ui.shared.TemplateWidgetMapper; import org.jboss.errai.ui.shared.api.annotations.DataField; import org.jboss.errai.ui.shared.api.annotations.Templated; import com.google.common.base.Strings; import com.google.gwt.dom.client.Element; import com.google.gwt.user.client.ui.Widget; /** * Store all injected {@link DataField} {@link Statement} instances into the * aggregate {@link Map} for this composite {@link Template}. * * @author <a href="mailto:lincolnbaxter@gmail.com">Lincoln Baxter, III</a> */ @CodeDecorator public class DataFieldCodeDecorator extends IOCDecoratorExtension<DataField> { public DataFieldCodeDecorator(final Class<DataField> decoratesWith) { super(decoratesWith); } @Override public void generateDecorator(final Decorable decorable, final FactoryController controller) { controller.ensureMemberExposed(decorable.get()); Statement instance = decorable.getAccessStatement(); final String name = getTemplateDataFieldName((DataField) decorable.getAnnotation(), decorable.getName()); final boolean isWidget = decorable.getType().isAssignableTo(Widget.class); if (!isWidget && decorable.getType().isAnnotationPresent(Templated.class)) { instance = Stmt.invokeStatic(TemplateWidgetMapper.class, "get", instance); } else if (decorable.getType().isAssignableTo(Element.class)) { instance = Stmt.invokeStatic(ElementWrapperWidget.class, "getWidget", instance); } else if (decorable.getType().isAssignableTo(IsElement.class)) { instance = Stmt.invokeStatic(ElementWrapperWidget.class, "getWidget", Stmt.nestedCall(instance).invoke("getElement")); } else if (RebindUtil.isNativeJsType(decorable.getType()) || RebindUtil.isElementalIface(decorable.getType())) { if (decorable.getType().isAssignableTo(HasValue.class)) { final MetaClass valueType = decorable.getType().getMethod("getValue", new Class[0]).getReturnType(); instance = Stmt.invokeStatic(ElementWrapperWidget.class, "getWidget", Stmt.invokeStatic(TemplateUtil.class, "asElement", instance), loadLiteral(valueType)); } else { instance = Stmt.invokeStatic(ElementWrapperWidget.class, "getWidget", Stmt.invokeStatic(TemplateUtil.class, "asElement", instance)); } } else if (decorable.getType().isAssignableTo( IsWidget.class )) { instance = Stmt.nestedCall( instance ).invoke( "asWidget" ); } else { if ( !isWidget ) { throw new GenerationException( "Unable to use [" + name + "] in class [" + decorable.getDecorableDeclaringType() + "] as a @DataField. The field must be a Widget, IsWidget, or a DOM element as either a JavaScriptObject, native @JsType, or IsElement." ); } } saveDataField(decorable, decorable.getType(), name, decorable.getName(), instance); } private void saveDataField(final Decorable decorable, final MetaClass type, final String name, final String fieldName, final Statement instance) { dataFieldMap(decorable.getInjectionContext(), decorable.getDecorableDeclaringType()).put(name, instance); dataFieldTypeMap(decorable.getInjectionContext(), decorable.getDecorableDeclaringType()).put(name, type); dataFieldAnnotationMap(decorable.getInjectionContext(), decorable.getDecorableDeclaringType()).put(name, (DataField) decorable.getAnnotation()); } private String getTemplateDataFieldName(final DataField annotation, final String deflt) { final String value = Strings.nullToEmpty(annotation.value()).trim(); return value.isEmpty() ? deflt : value; } /** * Get the map of {@link DataField} names and {@link Statement} instances. */ @SuppressWarnings("unchecked") private static Map<String, Statement> dataFieldMap(final InjectionContext context, final MetaClass templateType) { final String dataFieldMapName = dataFieldMapName(templateType); Map<String, Statement> dataFields = (Map<String, Statement>) context.getAttribute( dataFieldMapName); if (dataFields == null) { dataFields = new LinkedHashMap<>(); context.setAttribute(dataFieldMapName, dataFields); } return dataFields; } /** * Get the map of {@link DataField} names and {@link MetaClass} types. */ @SuppressWarnings("unchecked") private static Map<String, MetaClass> dataFieldTypeMap(final InjectionContext context, final MetaClass templateType) { final String dataFieldTypeMapName = dataFieldTypeMapName(templateType); Map<String, MetaClass> dataFieldTypes = (Map<String, MetaClass>) context.getAttribute( dataFieldTypeMapName); if (dataFieldTypes == null) { dataFieldTypes = new LinkedHashMap<>(); context.setAttribute(dataFieldTypeMapName, dataFieldTypes); } return dataFieldTypes; } /** * Get the map of {@link DataField} fields/parameters to {@link DataField} instances. */ private static Map<String, DataField> dataFieldAnnotationMap(final InjectionContext context, final MetaClass templateType) { final String dataFieldAnnoMapName = dataFieldAnnotationMapName(templateType); @SuppressWarnings("unchecked") Map<String, DataField> dataFieldTypes = (Map<String, DataField>) context.getAttribute(dataFieldAnnoMapName); if (dataFieldTypes == null) { dataFieldTypes = new LinkedHashMap<>(); context.setAttribute(dataFieldAnnoMapName, dataFieldTypes); } return dataFieldTypes; } /** * Get the aggregate map of {@link DataField} names and {@link Statement} * instances for the given {@link MetaClass} type and all ancestors returned * by {@link MetaClass#getSuperClass()}. */ @SuppressWarnings("unchecked") public static Map<String, Statement> aggregateDataFieldMap(final Decorable decorable, final MetaClass componentType) { final Map<String, Statement> result = new LinkedHashMap<>(); if (componentType.getSuperClass() != null) { result.putAll(aggregateDataFieldMap(decorable, componentType.getSuperClass())); } final Map<String, Statement> dataFields = (Map<String, Statement>) decorable.getInjectionContext().getAttribute( dataFieldMapName(componentType)); if (dataFields != null) { result.putAll(dataFields); } return result; } /** * Get the aggregate map of {@link DataField} names and {@link MetaClass} * types for the given {@link MetaClass} component type and all ancestors * returned by {@link MetaClass#getSuperClass()}. */ @SuppressWarnings("unchecked") public static Map<String, MetaClass> aggregateDataFieldTypeMap(final Decorable decorable, final MetaClass componentType) { final Map<String, MetaClass> result = new LinkedHashMap<>(); if (componentType.getSuperClass() != null) { result.putAll(aggregateDataFieldTypeMap(decorable, componentType.getSuperClass())); } final Map<String, MetaClass> dataFields = (Map<String, MetaClass>) decorable.getInjectionContext().getAttribute( dataFieldTypeMapName(componentType)); if (dataFields != null) { result.putAll(dataFields); } return result; } /** * Get the aggregate map of {@link DataField} names to instances for the given {@link MetaClass} component type and * all ancestors returned by {@link MetaClass#getSuperClass()}. */ public static Map<String, DataField> aggregateDataFieldAnnotationMap(final Decorable decorable, final MetaClass componentType) { final Map<String, DataField> result = new LinkedHashMap<>(); if (componentType.getSuperClass() != null) { result.putAll(aggregateDataFieldAnnotationMap(decorable, componentType.getSuperClass())); } @SuppressWarnings("unchecked") final Map<String, DataField> dataFields = (Map<String, DataField>) decorable.getInjectionContext().getAttribute( dataFieldAnnotationMapName(componentType)); if (dataFields != null) { result.putAll(dataFields); } return result; } /** * Using the given composite {@link Template} type, return the name of the map * of {@link DataField} names and variable {@link Statement} instances. */ private static final String dataFieldMapName(final MetaClass composite) { return DataFieldCodeDecorator.class.getName() + "_DATA_FIELD_MAP_" + composite.getFullyQualifiedName(); } /** * Using the given composite {@link Template} type, return the name of the map * of {@link DataField} names and variable {@link MetaClass} types. */ private static final String dataFieldTypeMapName(final MetaClass composite) { return DataFieldCodeDecorator.class.getName() + "_DATA_FIELD_TYPE_MAP_" + composite.getFullyQualifiedName(); } /** * Using the given composite {@link Template} type, return the name of the map * of {@link DataField} names and variable {@link MetaClass} types. */ private static final String dataFieldAnnotationMapName(final MetaClass composite) { return DataFieldCodeDecorator.class.getName() + "_DATA_FIELD_ANNOTATION_MAP_" + composite.getFullyQualifiedName(); } }