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