All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.gwt.autobean.rebind.model.AutoBeanFactoryModel Maven / Gradle / Ivy

/*
 * Copyright 2010 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.autobean.rebind.model;

import com.google.gwt.autobean.shared.AutoBean;
import com.google.gwt.autobean.shared.AutoBeanFactory;
import com.google.gwt.autobean.shared.AutoBeanFactory.Category;
import com.google.gwt.autobean.shared.AutoBeanFactory.NoWrap;
import com.google.gwt.autobean.shared.impl.EnumMap.ExtraEnums;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JEnumConstant;
import com.google.gwt.core.ext.typeinfo.JEnumType;
import com.google.gwt.core.ext.typeinfo.JGenericType;
import com.google.gwt.core.ext.typeinfo.JMethod;
import com.google.gwt.core.ext.typeinfo.JParameter;
import com.google.gwt.core.ext.typeinfo.JParameterizedType;
import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.editor.rebind.model.ModelUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 

AutoBeans has moved to * com.google.web.bindery.autobeans. This package will be * removed in a future version of GWT.

*/ @Deprecated public class AutoBeanFactoryModel { private static final JType[] EMPTY_JTYPE = new JType[0]; private final JGenericType autoBeanInterface; private final JClassType autoBeanFactoryInterface; private final Map allEnumConstants = new LinkedHashMap(); private final List categoryTypes; private final List noWrapTypes; private final TreeLogger logger; private final List methods = new ArrayList(); private final List objectMethods; private final TypeOracle oracle; private final Map peers = new LinkedHashMap(); private boolean poisoned; /** * Accumulates bean types that are reachable through the type graph. */ private Set toCalculate = new LinkedHashSet(); public AutoBeanFactoryModel(TreeLogger logger, JClassType factoryType) throws UnableToCompleteException { this.logger = logger; oracle = factoryType.getOracle(); autoBeanInterface = oracle.findType(AutoBean.class.getCanonicalName()).isGenericType(); autoBeanFactoryInterface = oracle.findType( AutoBeanFactory.class.getCanonicalName()).isInterface(); /* * We want to allow the user to override some of the useful Object methods, * so we'll extract them here. */ JClassType objectType = oracle.getJavaLangObject(); objectMethods = Arrays.asList( objectType.findMethod("equals", new JType[] {objectType}), objectType.findMethod("hashCode", EMPTY_JTYPE), objectType.findMethod("toString", EMPTY_JTYPE)); // Process annotations { Category categoryAnnotation = factoryType.getAnnotation(Category.class); if (categoryAnnotation != null) { categoryTypes = new ArrayList( categoryAnnotation.value().length); processClassArrayAnnotation(categoryAnnotation.value(), categoryTypes); } else { categoryTypes = null; } noWrapTypes = new ArrayList(); noWrapTypes.add(oracle.findType(AutoBean.class.getCanonicalName())); NoWrap noWrapAnnotation = factoryType.getAnnotation(NoWrap.class); if (noWrapAnnotation != null) { processClassArrayAnnotation(noWrapAnnotation.value(), noWrapTypes); } ExtraEnums extraEnumsAnnotation = factoryType.getAnnotation(ExtraEnums.class); if (extraEnumsAnnotation != null) { for (Class clazz : extraEnumsAnnotation.value()) { JEnumType asEnum = oracle.findType(clazz.getCanonicalName()).isEnum(); assert asEnum != null; for (JEnumConstant value : asEnum.getEnumConstants()) { allEnumConstants.put(value, AutoBeanMethod.getEnumName(value)); } } } } for (JMethod method : factoryType.getOverridableMethods()) { if (method.getEnclosingType().equals(autoBeanFactoryInterface)) { // Ignore methods in AutoBeanFactory continue; } JClassType returnType = method.getReturnType().isInterface(); if (returnType == null) { poison("The return type of method %s is a primitive type", method.getName()); continue; } // AutoBean blah() --> beanType = FooIntf JClassType beanType = ModelUtils.findParameterizationOf( autoBeanInterface, returnType)[0]; if (beanType.isInterface() == null) { poison("The %s parameterization is not an interface", beanType.getQualifiedSourceName()); continue; } // AutoBean blah(FooIntfSub foo) --> toWrap = FooIntfSub JClassType toWrap; if (method.getParameters().length == 0) { toWrap = null; } else if (method.getParameters().length == 1) { toWrap = method.getParameters()[0].getType().isClassOrInterface(); if (!beanType.isAssignableFrom(toWrap)) { poison( "The %s parameterization %s is not assignable from the delegate" + " type %s", autoBeanInterface.getSimpleSourceName(), toWrap.getQualifiedSourceName()); continue; } } else { poison("Unexpecetd parameters in method %s", method.getName()); continue; } AutoBeanType autoBeanType = getAutoBeanType(beanType); // Must wrap things that aren't simple interfaces if (!autoBeanType.isSimpleBean() && toWrap == null) { if (categoryTypes != null) { poison("The %s parameterization is not simple and the following" + " methods did not have static implementations:", beanType.getQualifiedSourceName()); for (AutoBeanMethod missing : autoBeanType.getMethods()) { if (missing.getAction().equals(JBeanMethod.CALL) && missing.getStaticImpl() == null) { poison(missing.getMethod().getReadableDeclaration()); } } } else { poison("The %s parameterization is not simple, but the %s method" + " does not provide a delegate", beanType.getQualifiedSourceName(), method.getName()); } continue; } AutoBeanFactoryMethod.Builder builder = new AutoBeanFactoryMethod.Builder(); builder.setAutoBeanType(autoBeanType); builder.setMethod(method); methods.add(builder.build()); } while (!toCalculate.isEmpty()) { Set examine = toCalculate; toCalculate = new LinkedHashSet(); for (JClassType beanType : examine) { getAutoBeanType(beanType); } } if (poisoned) { die("Unable to complete due to previous errors"); } } public Collection getAllTypes() { return Collections.unmodifiableCollection(peers.values()); } public List getCategoryTypes() { return categoryTypes; } public Map getEnumTokenMap() { return Collections.unmodifiableMap(allEnumConstants); } public List getMethods() { return Collections.unmodifiableList(methods); } public AutoBeanType getPeer(JClassType beanType) { beanType = ModelUtils.ensureBaseType(beanType); return peers.get(beanType); } private List computeMethods(JClassType beanType) { List toExamine = new ArrayList(); toExamine.addAll(Arrays.asList(beanType.getInheritableMethods())); toExamine.addAll(objectMethods); List toReturn = new ArrayList( toExamine.size()); for (JMethod method : toExamine) { if (method.isPrivate()) { // Ignore private methods continue; } AutoBeanMethod.Builder builder = new AutoBeanMethod.Builder(); builder.setMethod(method); // See if this method shouldn't have its return type wrapped // TODO: Allow class return types? JClassType classReturn = method.getReturnType().isInterface(); if (classReturn != null) { maybeCalculate(classReturn); if (noWrapTypes != null) { for (JClassType noWrap : noWrapTypes) { if (noWrap.isAssignableFrom(classReturn)) { builder.setNoWrap(true); break; } } } } // GET, SET, or CALL JBeanMethod action = JBeanMethod.which(method); builder.setAction(action); if (JBeanMethod.CALL.equals(action)) { JMethod staticImpl = findStaticImpl(beanType, method); if (staticImpl == null && objectMethods.contains(method)) { // Don't complain about lack of implementation for Object methods continue; } builder.setStaticImp(staticImpl); } AutoBeanMethod toAdd = builder.build(); // Collect referenced enums if (toAdd.hasEnumMap()) { allEnumConstants.putAll(toAdd.getEnumMap()); } // See if parameterizations will pull in more types if (toAdd.isCollection()) { maybeCalculate(toAdd.getElementType()); } else if (toAdd.isMap()) { maybeCalculate(toAdd.getKeyType()); maybeCalculate(toAdd.getValueType()); } toReturn.add(toAdd); } return toReturn; } private void die(String message) throws UnableToCompleteException { poison(message); throw new UnableToCompleteException(); } /** * Find Object __intercept(AutoBean<?> bean, Object value); in * the category types. */ private JMethod findInterceptor(JClassType beanType) { if (categoryTypes == null) { return null; } for (JClassType category : categoryTypes) { for (JMethod method : category.getOverloads("__intercept")) { // Ignore non-static, non-public methods // TODO: Implement visibleFrom() to allow package-protected categories if (!method.isStatic() || !method.isPublic()) { continue; } JParameter[] params = method.getParameters(); if (params.length != 2) { continue; } if (!methodAcceptsAutoBeanAsFirstParam(beanType, method)) { continue; } JClassType value = params[1].getType().isClassOrInterface(); if (value == null) { continue; } if (!oracle.getJavaLangObject().isAssignableTo(value)) { continue; } return method; } } return null; } /** * Search the category types for a static implementation of an interface * method. Given the interface method declaration: * *
   * Foo bar(Baz baz);
   * 
* * this will search the types in {@link #categoryTypes} for the following * method: * *
   * public static Foo bar(AutoBean<Intf> bean, Baz baz) {}
   * 
*/ private JMethod findStaticImpl(JClassType beanType, JMethod method) { if (categoryTypes == null) { return null; } for (JClassType category : categoryTypes) { // One extra argument for the AutoBean JParameter[] methodParams = method.getParameters(); int requiredArgs = methodParams.length + 1; overload : for (JMethod overload : category.getOverloads(method.getName())) { if (!overload.isStatic() || !overload.isPublic()) { // Ignore non-static, non-public methods continue; } JParameter[] overloadParams = overload.getParameters(); if (overloadParams.length != requiredArgs) { continue; } if (!methodAcceptsAutoBeanAsFirstParam(beanType, overload)) { // Ignore if the first parameter is a primitive or not assignable continue; } // Match the rest of the parameters for (int i = 1; i < requiredArgs; i++) { JType methodType = methodParams[i - 1].getType(); JType overloadType = overloadParams[i].getType(); if (methodType.equals(overloadType)) { // Match; exact, the usual case } else if (methodType.isClassOrInterface() != null && overloadType.isClassOrInterface() != null && methodType.isClassOrInterface().isAssignableTo( overloadType.isClassOrInterface())) { // Match; assignment-compatible } else { // No match, keep looking continue overload; } } return overload; } } return null; } private AutoBeanType getAutoBeanType(JClassType beanType) { beanType = ModelUtils.ensureBaseType(beanType); AutoBeanType toReturn = peers.get(beanType); if (toReturn == null) { AutoBeanType.Builder builder = new AutoBeanType.Builder(); builder.setOwnerFactory(this); builder.setPeerType(beanType); builder.setMethods(computeMethods(beanType)); builder.setInterceptor(findInterceptor(beanType)); if (noWrapTypes != null) { for (JClassType noWrap : noWrapTypes) { if (noWrap.isAssignableFrom(beanType)) { builder.setNoWrap(true); break; } } } toReturn = builder.build(); peers.put(beanType, toReturn); } return toReturn; } /** * Enqueue a type in {@link #toCalculate} if {@link #peers} does not already * contain an entry. */ private void maybeCalculate(JClassType type) { if (type.isInterface() == null || ModelUtils.isValueType(oracle, type)) { return; } if (!peers.containsKey(type)) { toCalculate.add(type); } } private boolean methodAcceptsAutoBeanAsFirstParam(JClassType beanType, JMethod method) { JParameter[] params = method.getParameters(); if (params.length == 0) { return false; } JClassType paramAsClass = params[0].getType().isClassOrInterface(); // First parameter is a primitive if (paramAsClass == null) { return false; } // Check using base types to account for erasure semantics JParameterizedType expectedFirst = oracle.getParameterizedType( autoBeanInterface, new JClassType[] {ModelUtils.ensureBaseType(beanType)}); return expectedFirst.isAssignableTo(paramAsClass); } private void poison(String message, Object... args) { logger.log(TreeLogger.ERROR, String.format(message, args)); poisoned = true; } private void processClassArrayAnnotation(Class[] classes, Collection accumulator) { for (Class clazz : classes) { JClassType category = oracle.findType(clazz.getCanonicalName()); if (category == null) { poison("Could not find @%s type %s in the TypeOracle", Category.class.getSimpleName(), clazz.getCanonicalName()); continue; } else if (!category.isPublic()) { poison("Category type %s is not public", category.getQualifiedSourceName()); continue; } else if (!category.isStatic() && category.isMemberType()) { poison("Category type %s must be static", category.getQualifiedSourceName()); continue; } accumulator.add(category); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy