| package org.eclipse.xtend.lib.annotations |
| |
| import com.google.common.annotations.Beta |
| import com.google.common.annotations.GwtCompatible |
| import com.google.common.collect.Sets |
| import java.lang.annotation.ElementType |
| import java.lang.annotation.Target |
| import java.util.List |
| import java.util.Set |
| import org.eclipse.xtend.lib.macro.Active |
| import org.eclipse.xtend.lib.macro.TransformationContext |
| import org.eclipse.xtend.lib.macro.TransformationParticipant |
| import org.eclipse.xtend.lib.macro.declaration.FieldDeclaration |
| import org.eclipse.xtend.lib.macro.declaration.InterfaceDeclaration |
| import org.eclipse.xtend.lib.macro.declaration.MemberDeclaration |
| import org.eclipse.xtend.lib.macro.declaration.MethodDeclaration |
| import org.eclipse.xtend.lib.macro.declaration.MutableMemberDeclaration |
| import org.eclipse.xtend.lib.macro.declaration.ResolvedMethod |
| import org.eclipse.xtend.lib.macro.declaration.TypeDeclaration |
| import org.eclipse.xtend.lib.macro.declaration.TypeReference |
| import java.util.Map |
| import java.lang.annotation.Documented |
| |
| /** |
| * Implements interfaces by forwarding method calls to an annotated field or method. |
| * |
| * <p> |
| * <pre> |
| * interface I { |
| * def String m() |
| * } |
| * class Foo implements I { |
| * override String m() { |
| * "Foo" |
| * } |
| * } |
| * class Bar implements I { |
| * //This will generate a method m(), which calls foo.m() |
| * @Delegate val foo = new Foo |
| * } |
| * </pre> |
| * |
| * For each interface that the declaring class and the delegate have in common, |
| * an implementation for each method is added if it does not yet exist. This |
| * implementation forwards all calls directly to the delegate. You can restrict |
| * which interfaces to implement using the {@link Class}[] value of this |
| * annotation. This is especially useful when there are several delegates that |
| * have some interfaces in common. |
| * |
| * </p> |
| * Delegate methods can either take |
| * <ul> |
| * <li>no arguments</li> |
| * <li>the name of the method to be called (String)</li> |
| * <li>the name of the method to be called (String), its parameter types |
| * (Class[]) and the actual arguments (Object[]) of the call</li> |
| * </ul> |
| * This allows you to generate meaningful error messages or to dynamically |
| * dispatch based on the arguments. |
| * |
| * @since 2.7 |
| */ |
| @Beta |
| @GwtCompatible |
| @Target(ElementType.FIELD, ElementType.METHOD) |
| @Active(DelegateProcessor) |
| @Documented |
| annotation Delegate { |
| /** |
| * Optional list of interfaces that this delegate is restricted to. |
| * Defaults to the common interfaces of the context type and the annotated |
| * element. |
| */ |
| Class<?>[] value = #[] |
| } |
| |
| /** |
| * @since 2.7 |
| * @noextend |
| * @noreference |
| */ |
| @Beta |
| class DelegateProcessor implements TransformationParticipant<MutableMemberDeclaration> { |
| |
| override doTransform(List<? extends MutableMemberDeclaration> elements, extension TransformationContext context) { |
| val extension util = new DelegateProcessor.Util(context) |
| elements.forEach [ |
| if (validDelegate) { |
| methodsToImplement.forEach[method|implementMethod(method)] |
| } |
| ] |
| } |
| |
| /** |
| * @since 2.7 |
| * @noextend |
| * @noreference |
| */ |
| @Beta |
| static class Util { |
| extension TransformationContext context |
| |
| new(TransformationContext context) { |
| this.context = context |
| } |
| |
| def dispatch isValidDelegate(FieldDeclaration it) { |
| hasValidType && !hasDelegationConflicts && areListedInterfacesValid |
| } |
| |
| def dispatch isValidDelegate(MethodDeclaration it) { |
| hasValidType && hasValidSignature && !hasDelegationConflicts && areListedInterfacesValid |
| } |
| |
| def hasValidType(MemberDeclaration it) { |
| if (type === null || type.inferred) { |
| addError("Cannot use inferred types on delegates") |
| false |
| } else { |
| true |
| } |
| } |
| |
| def dispatch getType(FieldDeclaration it) { |
| type |
| } |
| |
| def dispatch getType(MethodDeclaration it) { |
| returnType |
| } |
| |
| def hasValidSignature(MethodDeclaration it) { |
| switch parameters.map[type].toList { |
| case #[], |
| case #[string], |
| case #[string, Class.newTypeReference(newWildcardTypeReference).newArrayTypeReference, object.newArrayTypeReference]: |
| true |
| default: { |
| addError("Not a valid delegate signature, use () or (String methodName) or (String methodName, Class<?>[] argumentTypes, Object[] arguments)") |
| false |
| } |
| } |
| } |
| |
| def hasDelegationConflicts(MemberDeclaration delegate) { |
| var conflict = false |
| for (other : delegate.otherDelegates) { |
| val otherInterfaces = other.delegatedInterfaces |
| for (iface : delegate.delegatedInterfaces) { |
| if (otherInterfaces.contains(iface)) { |
| conflict = true |
| delegate.addError('''The interface «iface.simpleName» is also implemented by the delegate «other.simpleName»''') |
| } |
| } |
| } |
| conflict |
| } |
| |
| def otherDelegates(MemberDeclaration delegate) { |
| delegate.declaringType.delegates.filter[it != delegate] |
| } |
| |
| def areListedInterfacesValid(MemberDeclaration delegate) { |
| val declaringType = delegate.declaringType.newSelfTypeReference |
| val interfacesOfDeclaringType = declaringType.implementedInterfaces |
| val availableInterfaces = delegate.type.implementedInterfaces |
| val listedInterfaces = delegate.listedInterfaces |
| var valid = true |
| for(iface : listedInterfaces) { |
| if (!availableInterfaces.exists[type == iface.type]) { |
| delegate.addError('''«delegate.type.simpleName» does not implement «iface.simpleName»''') |
| valid = false |
| } |
| if (!interfacesOfDeclaringType.exists[type == iface.type]) { |
| delegate.addError('''«declaringType.simpleName» does not implement «iface.simpleName»''') |
| valid = false |
| } |
| } |
| if (listedInterfaces.empty && Sets.intersection(interfacesOfDeclaringType, availableInterfaces).empty) { |
| delegate.addError('''«delegate.type.simpleName» and «declaringType.simpleName» have no interfaces in common''') |
| valid = false |
| } |
| valid |
| } |
| |
| def getDelegates(TypeDeclaration it) { |
| declaredMembers.filter[findAnnotation(findTypeGlobally(Delegate)) !== null] |
| } |
| |
| def listedInterfaces(MemberDeclaration it) { |
| findAnnotation(findTypeGlobally(Delegate)).getClassArrayValue("value").toSet |
| } |
| |
| def Set<TypeReference> getImplementedInterfaces(TypeReference it) { |
| val seen = newLinkedHashSet |
| collectAllSuperTypes(seen) |
| seen.filter[type instanceof InterfaceDeclaration].toSet |
| } |
| |
| private def void collectAllSuperTypes(TypeReference it, Set<TypeReference> seen) { |
| val cycle = !seen.add(it) |
| if (cycle) |
| return; |
| declaredSuperTypes.forEach[collectAllSuperTypes(seen)] |
| } |
| |
| def getDelegatedInterfaces(MemberDeclaration delegate) { |
| val interfacesOfDeclaringType = delegate.declaringType.newSelfTypeReference.implementedInterfaces |
| val listedInterfaces = delegate.listedInterfaces |
| val availableInterfaces = delegate.type.implementedInterfaces |
| availableInterfaces.filter[iface| |
| interfacesOfDeclaringType.contains(iface) |
| && (listedInterfaces.empty || listedInterfaces.exists[iface.isAssignableFrom(it)]) |
| ].toSet |
| } |
| |
| def getMethodsToImplement(MemberDeclaration delegate) { |
| delegate.delegatedInterfaces.map[declaredResolvedMethods].flatten |
| .filter[delegate.declaringType.findDeclaredMethod(declaration.simpleName, resolvedParameters.map[resolvedType]) === null] |
| .filter[!isObjectMethod] |
| .filter[!isStatic] |
| .groupBy[simpleSignature].values.map[head] |
| .sortBy[simpleSignature] |
| .toSet |
| } |
| |
| def isObjectMethod(ResolvedMethod it) { |
| val name = declaration.simpleName |
| val parameterTypes = resolvedParameters.map[resolvedType].toList |
| |
| name == "hashCode" && parameterTypes.empty |
| || name == "toString" && parameterTypes.empty |
| || name == "equals" && parameterTypes == #[object] |
| || name == "finalize" && parameterTypes.empty |
| || name == "clone" && parameterTypes.empty |
| } |
| |
| def isStatic(ResolvedMethod it) { |
| declaration.isStatic |
| } |
| |
| def implementMethod(MutableMemberDeclaration delegate, ResolvedMethod resolvedMethod) { |
| delegate.markAsRead |
| val declaration = resolvedMethod.declaration |
| delegate.declaringType.addMethod(declaration.simpleName) [ impl | |
| impl.primarySourceElement = delegate.primarySourceElement |
| val typeParameterMappings = newHashMap |
| resolvedMethod.resolvedTypeParameters.forEach[param| |
| val copy = impl.addTypeParameter(param.declaration.simpleName, param.resolvedUpperBounds) |
| typeParameterMappings.put(param.declaration.newTypeReference, copy.newTypeReference) |
| copy.upperBounds = copy.upperBounds.map[replace(typeParameterMappings)] |
| ] |
| impl.exceptions = resolvedMethod.resolvedExceptionTypes.map[replace(typeParameterMappings)] |
| impl.varArgs = declaration.varArgs |
| impl.returnType = resolvedMethod.resolvedReturnType.replace(typeParameterMappings) |
| resolvedMethod.resolvedParameters.forEach[p|impl.addParameter(p.declaration.simpleName, p.resolvedType.replace(typeParameterMappings))] |
| impl.body = ''' |
| «resolvedMethod.returnIfNeeded»«delegate.delegateAccess(declaration)».«declaration.simpleName»(«declaration.parameters.join(", ")[simpleName]»); |
| ''' |
| ] |
| } |
| |
| def TypeReference replace(TypeReference target, Map<? extends TypeReference, ? extends TypeReference> mappings) { |
| mappings.entrySet.fold(target)[result, mapping|result.replace(mapping.key, mapping.value)] |
| } |
| |
| def TypeReference replace(TypeReference target, TypeReference oldType, TypeReference newType) { |
| if (target == oldType) |
| return newType |
| if (!target.actualTypeArguments.isEmpty) |
| return newTypeReference(target.type, target.actualTypeArguments.map[replace(oldType, newType)]) |
| if (target.wildCard) { |
| if (target.upperBound != object) |
| return target.upperBound.replace(oldType, newType).newWildcardTypeReference |
| else if (!target.lowerBound.isAnyType) |
| return target.lowerBound.replace(oldType, newType).newWildcardTypeReferenceWithLowerBound |
| } |
| if (target.isArray) |
| return target.arrayComponentType.replace(oldType, newType).newArrayTypeReference |
| return target |
| } |
| |
| def dispatch delegateAccess(FieldDeclaration it, MethodDeclaration method) { |
| '''this.«simpleName»''' |
| } |
| |
| def dispatch delegateAccess(MethodDeclaration it, MethodDeclaration method) { |
| switch parameters.map[type].toList { |
| case #[]: |
| '''this.«simpleName»()''' |
| case #[string]: |
| '''this.«simpleName»("«method.simpleName»")''' |
| case #[string, Class.newTypeReference(newWildcardTypeReference).newArrayTypeReference, object.newArrayTypeReference]: { |
| '''this.«simpleName»("«method.simpleName»", new Class[]{«method.parameters.join(", ")[type.type.simpleName + ".class"]»}, new Object[]{«method.parameters.join(", ")[simpleName]»})''' |
| } |
| default: |
| throw new IllegalArgumentException("delegate signature") |
| } |
| } |
| |
| def returnIfNeeded(ResolvedMethod it) { |
| if(resolvedReturnType.isVoid) "" else "return " |
| } |
| } |
| |
| } |