| /* | |
| * Copyright 2009 Google Inc. | |
| * | |
| * 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 com.google.gwt.uibinder.rebind.model; | |
| import com.google.gwt.core.ext.UnableToCompleteException; | |
| import com.google.gwt.core.ext.typeinfo.JClassType; | |
| import com.google.gwt.core.ext.typeinfo.JField; | |
| import com.google.gwt.core.ext.typeinfo.JGenericType; | |
| import com.google.gwt.core.ext.typeinfo.JMethod; | |
| import com.google.gwt.core.ext.typeinfo.JParameterizedType; | |
| import com.google.gwt.uibinder.client.UiFactory; | |
| import com.google.gwt.uibinder.client.UiField; | |
| import com.google.gwt.uibinder.client.UiHandler; | |
| import com.google.gwt.uibinder.rebind.MortalLogger; | |
| import com.google.gwt.uibinder.rebind.UiBinderContext; | |
| import java.util.ArrayList; | |
| import java.util.Collection; | |
| import java.util.HashMap; | |
| import java.util.List; | |
| import java.util.Map; | |
| import java.util.TreeMap; | |
| /** | |
| * Model class with all attributes of the owner class. | |
| * This includes factories, fields and handlers. | |
| */ | |
| public class OwnerClass { | |
| /** | |
| * Map from field name to model. | |
| */ | |
| private final Map<String, OwnerField> uiFields = | |
| new TreeMap<String, OwnerField>(); | |
| /** | |
| * Map from field type to model. | |
| * | |
| * This is used for binding resources - for widgets, there may be multiple | |
| * widgets with the same type, in which case this becomes meaningless. | |
| */ | |
| private final Map<JClassType, OwnerField> uiFieldTypes = | |
| new HashMap<JClassType, OwnerField>(); | |
| /** | |
| * Map from type to the method that produces it. | |
| */ | |
| private final Map<JClassType, JMethod> uiFactories = | |
| new HashMap<JClassType, JMethod>(); | |
| /** | |
| * List of all @UiHandler methods in the owner class. | |
| */ | |
| private final List<JMethod> uiHandlers = new ArrayList<JMethod>(); | |
| private final MortalLogger logger; | |
| private final JClassType ownerType; | |
| private final UiBinderContext context; | |
| /** | |
| * Constructor. | |
| * | |
| * @param ownerType the type of the owner class | |
| * @param logger | |
| */ | |
| public OwnerClass(JClassType ownerType, MortalLogger logger, | |
| UiBinderContext context) throws UnableToCompleteException { | |
| this.logger = logger; | |
| this.ownerType = ownerType; | |
| this.context = context; | |
| findUiFields(ownerType); | |
| findUiFactories(ownerType); | |
| findUiHandlers(ownerType); | |
| } | |
| public JClassType getOwnerType() { | |
| return ownerType; | |
| } | |
| /** | |
| * Returns the method annotated with @UiFactory which returns the given type. | |
| * | |
| * @param forType the type to look for a factory of | |
| * @return the factory method, or null if none exists | |
| */ | |
| public JMethod getUiFactoryMethod(JClassType forType) { | |
| JGenericType genericType = forType.isGenericType(); | |
| if (genericType != null) { | |
| forType = genericType.getRawType(); | |
| } | |
| return uiFactories.get(forType); | |
| } | |
| /** | |
| * Gets a field with the given name. | |
| * It's important to notice that a field may not exist on the owner class even | |
| * if it has a name in the XML and even has handlers attached to it - such a | |
| * field will only exist in the generated binder class. | |
| * | |
| * @param name the name of the field to get | |
| * @return the field descriptor, or null if the owner doesn't have that field | |
| */ | |
| public OwnerField getUiField(String name) { | |
| return uiFields.get(name); | |
| } | |
| /** | |
| * Gets the field with the given type. | |
| * Note that multiple fields can have the same type, so it only makes sense to | |
| * call this to retrieve resource fields, such as messages and image bundles, | |
| * for which only one instance is expected. | |
| * | |
| * @param type the type of the field | |
| * @return the field descriptor | |
| * @deprecated This will die with {@link com.google.gwt.uibinder.attributeparsers.BundleAttributeParser} | |
| */ | |
| @Deprecated | |
| public OwnerField getUiFieldForType(JClassType type) { | |
| return uiFieldTypes.get(type); | |
| } | |
| /** | |
| * Returns a collection of all fields in the owner class. | |
| */ | |
| public Collection<OwnerField> getUiFields() { | |
| return uiFields.values(); | |
| } | |
| /** | |
| * Returns all the UiHandler methods defined in the owner class. | |
| */ | |
| public List<JMethod> getUiHandlers() { | |
| return uiHandlers; | |
| } | |
| /** | |
| * Scans the owner class to find all methods annotated with @UiFactory, and | |
| * puts them in {@link #uiFactories}. | |
| * | |
| * @param ownerType the type of the owner class | |
| * @throws UnableToCompleteException | |
| */ | |
| private void findUiFactories(JClassType ownerType) | |
| throws UnableToCompleteException { | |
| JMethod[] methods = ownerType.getMethods(); | |
| for (JMethod method : methods) { | |
| if (method.isAnnotationPresent(UiFactory.class)) { | |
| JClassType factoryType = method.getReturnType().isClassOrInterface(); | |
| if (factoryType == null) { | |
| logger.die("Factory return type is not a class in method " | |
| + method.getName()); | |
| } | |
| JParameterizedType paramType = factoryType.isParameterized(); | |
| if (paramType != null) { | |
| factoryType = paramType.getRawType(); | |
| } | |
| if (uiFactories.containsKey(factoryType)) { | |
| logger.die("Duplicate factory in class " | |
| + method.getEnclosingType().getName() + " for type " | |
| + factoryType.getName()); | |
| } | |
| uiFactories.put(factoryType, method); | |
| } | |
| } | |
| // Recurse to superclass | |
| JClassType superclass = ownerType.getSuperclass(); | |
| if (superclass != null) { | |
| findUiFactories(superclass); | |
| } | |
| } | |
| /** | |
| * Scans the owner class to find all fields annotated with @UiField, and puts | |
| * them in {@link #uiFields} and {@link #uiFieldTypes}. | |
| * | |
| * @param ownerType the type of the owner class | |
| */ | |
| private void findUiFields(JClassType ownerType) | |
| throws UnableToCompleteException { | |
| JField[] fields = ownerType.getFields(); | |
| for (JField field : fields) { | |
| if (field.isAnnotationPresent(UiField.class)) { | |
| JClassType ownerFieldType = field.getType().isClassOrInterface(); | |
| if (ownerFieldType == null) { | |
| logger.die("Field type is not a class in field " | |
| + field.getName()); | |
| } | |
| OwnerField ownerField = new OwnerField(field, logger, context); | |
| String ownerFieldName = field.getName(); | |
| uiFields.put(ownerFieldName, ownerField); | |
| uiFieldTypes.put(ownerFieldType, ownerField); | |
| } | |
| } | |
| // Recurse to superclass | |
| JClassType superclass = ownerType.getSuperclass(); | |
| if (superclass != null) { | |
| findUiFields(superclass); | |
| } | |
| } | |
| /** | |
| * Scans the owner class to find all methods annotated with @UiHandler, and | |
| * adds them to their respective fields. | |
| * | |
| * @param ownerType the type of the owner class | |
| */ | |
| private void findUiHandlers(JClassType ownerType) { | |
| JMethod[] methods = ownerType.getMethods(); | |
| for (JMethod method : methods) { | |
| if (method.isAnnotationPresent(UiHandler.class)) { | |
| uiHandlers.add(method); | |
| } | |
| } | |
| // Recurse to superclass | |
| JClassType superclass = ownerType.getSuperclass(); | |
| if (superclass != null) { | |
| findUiHandlers(superclass); | |
| } | |
| } | |
| } |