
com.google.gwt.inject.rebind.util.KeyUtil Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gin Show documentation
Show all versions of gin Show documentation
GIN (GWT INjection) brings automatic dependency injection to Google Web Toolkit client-side code. GIN is built on top of Guice and uses (a subset of) Guice's binding language.
/*
* 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.inject.rebind.util;
import com.google.gwt.core.ext.typeinfo.HasAnnotations;
import com.google.gwt.core.ext.typeinfo.JAbstractMethod;
import com.google.gwt.core.ext.typeinfo.JArrayType;
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.JParameter;
import com.google.gwt.core.ext.typeinfo.JParameterizedType;
import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.core.ext.typeinfo.JWildcardType;
import com.google.gwt.core.ext.typeinfo.NotFoundException;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.inject.rebind.binding.Injectable;
import com.google.gwt.inject.rebind.binding.RequiredKeys;
import com.google.inject.BindingAnnotation;
import com.google.inject.Inject;
import com.google.inject.Key;
import com.google.inject.ProvisionException;
import com.google.inject.Singleton;
import com.google.inject.util.Types;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.HashSet;
/**
* Util object that offers {@link Key} retrieval and manipulation methods.
*/
@Singleton
public class KeyUtil {
private final TypeOracle typeOracle;
private final NameGenerator nameGenerator;
private final MemberCollector memberCollector;
@Inject
public KeyUtil(TypeOracle typeOracle, NameGenerator nameGenerator,
@Injectable MemberCollector memberCollector) {
this.typeOracle = typeOracle;
this.nameGenerator = nameGenerator;
this.memberCollector = memberCollector;
}
public Key> getKey(JMethod method) {
if (isMemberInject(method)) {
return getKey(method.getParameters()[0]);
}
return getKey(method.getReturnType(), getAnnotations(JAbstractMethod.class, method));
}
public Key> getKey(JParameter param) {
return getKey(param.getType(), getAnnotations(JParameter.class, param));
}
public Key> getKey(JField field) {
return getKey(field.getType(), getAnnotations(JField.class, field));
}
public boolean isMemberInject(JMethod method) {
return method.getReturnType() == JPrimitiveType.VOID;
}
public Class> getRawType(Key> key) {
Type type = key.getTypeLiteral().getType();
if (type instanceof Class) {
return (Class) type;
} else if (type instanceof ParameterizedType) {
return (Class) ((ParameterizedType) type).getRawType();
}
throw new ProvisionException("Can't get raw type for " + key);
}
public JClassType getRawClassType(Key> key) {
return getClassType(getRawType(key));
}
public JClassType getClassType(Key> key) {
return getClassType(key.getTypeLiteral().getType());
}
public JClassType getClassType(Type type) {
if (type instanceof Class) {
return typeOracle.findType(((Class) type).getCanonicalName());
} else if (type instanceof ParameterizedType) {
ParameterizedType parameterized = (ParameterizedType) type;
JClassType[] parameters = new JClassType[parameterized.getActualTypeArguments().length];
int i = 0;
for (Type paramType : parameterized.getActualTypeArguments()) {
parameters[i++] = getClassType(paramType);
}
Class rawClass = (Class) parameterized.getRawType();
JClassType classType =
typeOracle.findType(rawClass.getCanonicalName());
JGenericType genericType = classType.isGenericType();
if (genericType == null) {
throw new ProvisionException("Can't get class type for " + type);
}
return typeOracle.getParameterizedType(genericType, genericType.getEnclosingType(),
parameters);
}
throw new ProvisionException("Can't get class type for " + type);
}
/**
* Gets the Guice binding key for a given GWT type with optional annotations.
*
* @param gwtType GWT type to convert in to a key
* @param annotations Optional array of {@code Annotation}s. If this contains
* one and only one {@link BindingAnnotation}, it will be included in the
* key. If it includes more than one, an exception will be thrown.
* @return Guice Key instance for this type/annotations
* @throws ProvisionException in case of any failure
*/
public Key> getKey(JType gwtType, Annotation... annotations) throws ProvisionException {
try {
Type type = gwtTypeToJavaType(gwtType);
return getKey(type, annotations);
} catch (ClassNotFoundException e) {
throw new ProvisionException("Error creating key for " + gwtType, e);
} catch (NoSuchFieldException e) {
throw new ProvisionException("Error creating key for " + gwtType, e);
} catch (IllegalAccessException e) {
throw new ProvisionException("Error creating key for " + gwtType, e);
}
}
/**
* Gets the Guice binding key for a given Java type with optional annotations.
*
* @param type Java type to convert in to a key
* @param annotations Optional array of {@code Annotation}s. If this contains
* one and only one {@link BindingAnnotation}, it will be included in the
* key. If it includes more than one, an exception will be thrown.
* @return Guice Key instance for this type/annotations
* @throws ProvisionException in case of any failure
*/
public Key> getKey(Type type, Annotation... annotations) throws ProvisionException {
Annotation bindingAnnotation = getBindingAnnotation(annotations);
if (bindingAnnotation == null) {
return Key.get(type);
} else {
return Key.get(type, bindingAnnotation);
}
}
/**
* Returns a {@link JMethod} that represents the same method as the provided
* {@link Method} reflection object.
*
* @param javaMethod method as used by reflection
* @return method as used by the GWT compiler
* @throws NotFoundException if method cannot be found in source
*/
public JMethod javaToGwtMethod(Method javaMethod) throws NotFoundException {
JClassType gwtEnclosingType =
typeOracle.findType(javaMethod.getDeclaringClass().getCanonicalName());
JMethod resultingMethod = null;
for (JMethod gwtMethod : gwtEnclosingType.getMethods()) {
if (gwtMethod.getName().equals(javaMethod.getName())) {
JParameter[] gwtParameters = gwtMethod.getParameters();
Class>[] javaParameters = javaMethod.getParameterTypes();
if (gwtParameters.length != javaParameters.length) {
continue;
}
boolean found = true;
for (int i = 0; i < gwtParameters.length; i++) {
found = found && gwtParameters[i].getType().getQualifiedSourceName().equals(
javaParameters[i].getCanonicalName());
}
if (found) {
resultingMethod = gwtMethod;
break;
}
}
}
if (resultingMethod == null) {
throw new NotFoundException("Couldn't locate requested method in source: " + javaMethod);
}
return resultingMethod;
}
/**
* Returns a {@link JField} that represents the same method as the provided
* {@link Field} reflection object.
*
* @param javaField field as used by reflection
* @return field as used by the GWT compiler
*/
public JField javaToGwtField(Field javaField) {
JClassType gwtEnclosingType =
typeOracle.findType(javaField.getDeclaringClass().getCanonicalName());
return gwtEnclosingType.getField(javaField.getName());
}
/**
* Returns true if the passed class member has an {@literal @}{@code Inject}
* annotation and the injection is marked as optional (
* {@literal @}{@code Inject(optional=true)}).
*
* @param member member to be checked
* @return true if member is injected optionally
*/
public boolean isOptional(HasAnnotations member) {
Inject annot = member.getAnnotation(Inject.class);
return annot != null && annot.optional();
}
/**
* Collects and returns all keys required to inject the given class.
*
* @param classType class for which required keys are calculated
* @return keys required to inject given class
*/
public RequiredKeys getRequiredKeys(JClassType classType) {
Set> required = new HashSet>();
Set> optional = new HashSet>();
for (JMethod method : memberCollector.getMethods(classType)) {
RequiredKeys requiredKeys = getRequiredKeys(method);
required.addAll(requiredKeys.getRequiredKeys());
optional.addAll(requiredKeys.getOptionalKeys());
}
for (JField field : memberCollector.getFields(classType)) {
Key> key = getKey(field);
if (isOptional(field)) {
optional.add(key);
} else {
required.add(key);
}
}
return new RequiredKeys(required, optional);
}
/**
* Collects and returns all keys required to inject the given method.
*
* @param method method for which required keys are calculated
* @return required keys
*/
public RequiredKeys getRequiredKeys(JAbstractMethod method) {
Set> required = new HashSet>();
Set> optional = new HashSet>();
for (JParameter param : method.getParameters()) {
Key> key = getKey(param);
if (isOptional(method)) {
optional.add(key);
} else {
required.add(key);
}
}
return new RequiredKeys(required, optional);
}
private Annotation getBindingAnnotation(Annotation[] annotations) {
if (annotations == null || annotations.length == 0) {
return null;
}
Annotation bindingAnnotation = null;
for (Annotation annotation : annotations) {
if (annotation.annotationType().getAnnotation(BindingAnnotation.class) != null) {
if (bindingAnnotation != null) {
throw new ProvisionException(">1 binding annotation found: "
+ annotation + ", " + bindingAnnotation);
}
bindingAnnotation = annotation;
// Keep going so we can find any rogue additional binding annotations
}
}
return bindingAnnotation;
}
private Type gwtTypeToJavaType(JType gwtType)
throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
JPrimitiveType primitiveType = gwtType.isPrimitive();
if (primitiveType != null) {
return loadClass(primitiveType);
}
JArrayType arrayType = gwtType.isArray();
if (gwtType.isArray() != null) {
Type componentType = gwtTypeToJavaType(arrayType.getComponentType());
return Types.arrayOf(componentType);
}
JWildcardType wildcardType = gwtType.isWildcard();
if (wildcardType != null) {
Type baseType = gwtTypeToJavaType(wildcardType.getBaseType());
switch (wildcardType.getBoundType()) {
case EXTENDS:
return Types.subtypeOf(baseType);
case SUPER:
return Types.supertypeOf(baseType);
case UNBOUND:
}
}
JParameterizedType parameterizedType = gwtType.isParameterized();
if (gwtType.isParameterized() != null) {
JClassType[] typeArgs = parameterizedType.getTypeArgs();
List javaTypeArgs = new ArrayList();
for (JClassType typeArg : typeArgs) {
JWildcardType wildcard = typeArg.isWildcard();
if (wildcard == null || wildcard.getBoundType() != JWildcardType.BoundType.UNBOUND) {
javaTypeArgs.add(gwtTypeToJavaType(typeArg));
}
}
Type rawType = gwtTypeToJavaType(parameterizedType.getRawType());
if (parameterizedType.getEnclosingType() != null) {
return Types.newParameterizedTypeWithOwner(
gwtTypeToJavaType(parameterizedType.getEnclosingType()), rawType,
javaTypeArgs.toArray(new Type[javaTypeArgs.size()]));
} else {
return Types.newParameterizedType(rawType,
javaTypeArgs.toArray(new Type[javaTypeArgs.size()]));
}
}
JClassType jClassType = gwtType.isClassOrInterface();
if (gwtType.isClassOrInterface() != null) {
return loadClass(jClassType);
}
throw new ProvisionException("Unknown GWT type: " + gwtType);
}
// Wrapper around Class.forName that passes initialize=false. This is critical
// because GWT client code (whose class names we may be referencing here)
// can not necessarily have its static initializers run at rebind time.
private static Class> loadClass(JType type) throws ClassNotFoundException,
NoSuchFieldException, IllegalAccessException {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// Handle primitive types.
JPrimitiveType primitiveType = type.isPrimitive();
if (primitiveType != null) {
String boxClassName = primitiveType.getQualifiedBoxedSourceName();
Class> boxClass = Class.forName(boxClassName, false, classLoader);
return (Class) boxClass.getField("TYPE").get(null);
}
JClassType classType = type.isClassOrInterface();
if (classType == null) {
throw new UnsupportedOperationException("Cannot load " + type + ".");
}
return Class.forName(getBinaryName((JClassType) type), false, classLoader);
}
private static String getBinaryName(JClassType type) {
JClassType enclosingType = type.getEnclosingType();
if (enclosingType != null) {
return getBinaryName(enclosingType) + "$" + type.getSimpleSourceName();
}
return type.getQualifiedSourceName();
}
// Reflective hack until getAnnotations is exposed from GWT
private static Annotation[] getAnnotations(Class clazz, T instance) {
try {
Method method = clazz.getDeclaredMethod("getAnnotations");
method.setAccessible(true);
return (Annotation[]) method.invoke(instance);
} catch (NoSuchMethodException e) {
throw new ProvisionException("Failed to get annotations from " + instance, e);
} catch (IllegalAccessException e) {
throw new ProvisionException("Failed to get annotations from " + instance, e);
} catch (InvocationTargetException e) {
throw new ProvisionException("Failed to get annotations from " + instance, e);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy