ElementNameValuePairGen interfaceListSpecified = getAnnotationElement(declareMixinAnnotation, "interfaces");
List newParents = new ArrayList(1);
- List newParentTypes = new ArrayList(1);
+ List newInterfaceTypes = new ArrayList(1);
if (interfaceListSpecified != null) {
ArrayElementValueGen arrayOfInterfaceTypes = (ArrayElementValueGen) interfaceListSpecified.getValue();
int numberOfTypes = arrayOfInterfaceTypes.getElementValuesArraySize();
// TODO crappy replace required
ResolvedType ajInterfaceType = UnresolvedType.forSignature(interfaceType.getClassString().replace("/", "."))
.resolve(world);
- newParentTypes.add(ajInterfaceType);
if (ajInterfaceType.isMissing() || !ajInterfaceType.isInterface()) {
reportError(
"Types listed in the 'interfaces' DeclareMixin annotation value must be valid interfaces. This is invalid: "
+ ajInterfaceType.getName(), struct); // TODO better error location, use the method position
return false;
}
+ if (!ajInterfaceType.isAssignableFrom(methodReturnType)) {
+ reportError(getMethodForMessage(struct) + ": factory method does not return something that implements '"
+ + ajInterfaceType.getName() + "'", struct);
+ return false;
+ }
+ newInterfaceTypes.add(ajInterfaceType);
// Checking that it is a superinterface of the methods return value is done at weave time
TypePattern newParent = parseTypePattern(ajInterfaceType.getName(), struct);
newParents.add(newParent);
}
} else {
+ if (methodReturnType.isClass()) {
+ reportError(
+ getMethodForMessage(struct)
+ + ": factory methods for a mixin must either return an interface type or specify interfaces in the annotation and return a class",
+ struct);
+ return false;
+ }
// Use the method return type: this might be a class or an interface
TypePattern newParent = parseTypePattern(methodReturnType.getName(), struct);
- newParentTypes.add(methodReturnType);
+ newInterfaceTypes.add(methodReturnType);
newParents.add(newParent);
}
if (newParents.size() == 0) {
// TODO output warning
return false;
}
- // TODO DECLAREMIXIN set the location more accurately than
- // this
+
+ // Create the declare parents that will add the interfaces to matching targets
FormalBinding[] bindings = new org.aspectj.weaver.patterns.FormalBinding[0];
IScope binding = new BindingScope(struct.enclosingType, struct.context, bindings);
- // first add the declare implements like
// how do we mark this as a decp due to decmixin?
DeclareParents dp = new DeclareParentsMixin(targetTypePattern, newParents);
- dp.resolve(binding); // resolves the parent and child parts
- // of the decp
-
- // resolve this so that we can use it for the
- // MethodDelegateMungers below.
- // eg. '@Coloured *' will change from a WildTypePattern to
- // an 'AnyWithAnnotationTypePattern' after this
- // resolution
- targetTypePattern = dp.getChild();// targetTypePattern.resolveBindings(binding, Bindings.NONE, false, false);
- // TODO kick ISourceLocation sl =
- // struct.bField.getSourceLocation(); ??
- // dp.setLocation(dp.getDeclaringType().getSourceContext(),
- // dp.getDeclaringType().getSourceLocation().getOffset(),
- // dp.getDeclaringType().getSourceLocation().getOffset());
+ dp.resolve(binding);
+ targetTypePattern = dp.getChild();
+
dp.setLocation(struct.context, -1, -1); // not ideal...
struct.ajAttributes.add(new AjAttribute.DeclareAttribute(dp));
// The factory method for building the implementation is the
// one attached to the annotation:
Method implementationFactory = struct.method;
- // do we have a defaultImpl=xxx.class (ie implementation)
- // String defaultImplClassName = null;
- // ElementNameValuePairGen defaultImplNVP =
- // getAnnotationElement(declareMixinAnnotation,
- // "defaultImpl");
- // if (defaultImplNVP != null) {
- // ClassElementValueGen defaultImpl = (ClassElementValueGen)
- // defaultImplNVP.getValue();
- // defaultImplClassName =
- // UnresolvedType.forSignature(defaultImpl.getClassString()).getName();
- // if
- // (defaultImplClassName.equals("org.aspectj.lang.annotation.DeclareParents"))
- // {
- // defaultImplClassName = null;
- // } else {
- // // check public no arg ctor
- // ResolvedType impl =
- // struct.enclosingType.getWorld().resolve(defaultImplClassName,
- // false);
- // ResolvedMember[] mm = impl.getDeclaredMethods();
- // int implModifiers = impl.getModifiers();
- // boolean defaultVisibilityImpl =
- // !(Modifier.isPrivate(implModifiers)
- // || Modifier.isProtected(implModifiers) ||
- // Modifier.isPublic(implModifiers));
- // boolean hasNoCtorOrANoArgOne = true;
- // ResolvedMember foundOneOfIncorrectVisibility = null;
- // for (int i = 0; i < mm.length; i++) {
- // ResolvedMember resolvedMember = mm[i];
- // if (resolvedMember.getName().equals("<init>")) {
- // hasNoCtorOrANoArgOne = false;
- //
- // if (resolvedMember.getParameterTypes().length == 0) {
- // if (defaultVisibilityImpl) { // default visibility
- // implementation
- // if (resolvedMember.isPublic() ||
- // resolvedMember.isDefault()) {
- // hasNoCtorOrANoArgOne = true;
- // } else {
- // foundOneOfIncorrectVisibility = resolvedMember;
- // }
- // } else if (Modifier.isPublic(implModifiers)) { // public
- // implementation
- // if (resolvedMember.isPublic()) {
- // hasNoCtorOrANoArgOne = true;
- // } else {
- // foundOneOfIncorrectVisibility = resolvedMember;
- // }
- // }
- // }
- // }
- // if (hasNoCtorOrANoArgOne) {
- // break;
- // }
- // }
- // if (!hasNoCtorOrANoArgOne) {
- // if (foundOneOfIncorrectVisibility != null) {
- // reportError(
- // "@DeclareParents: defaultImpl=\""
- // + defaultImplClassName
- // +
- // "\" has a no argument constructor, but it is of incorrect visibility. It must be at least as visible as the type.",
- // struct);
- // } else {
- // reportError("@DeclareParents: defaultImpl=\"" +
- // defaultImplClassName
- // + "\" has no public no-arg constructor", struct);
- // }
- // }
- // if (!methodType.isAssignableFrom(impl)) {
- // reportError("@DeclareParents: defaultImpl=\"" +
- // defaultImplClassName
- // + "\" does not implement the interface '" +
- // methodType.toString() + "'", struct);
- // }
- // }
- //
- // }
- // then iterate on field interface hierarchy (not object)
boolean hasAtLeastOneMethod = false;
- for (Iterator iterator = newParentTypes.iterator(); iterator.hasNext();) {
+ for (Iterator iterator = newInterfaceTypes.iterator(); iterator.hasNext();) {
ResolvedType typeForDelegation = (ResolvedType) iterator.next();
// TODO check for overlapping interfaces. Eg. A implements I, I extends J - if they specify interfaces={I,J} we dont
// want to do any methods twice
for (int i = 0; i < methods.length; i++) {
ResolvedMember method = methods[i];
if (method.isAbstract()) {
- // moved to be detected at weave time if the target
- // doesnt implement the methods
- // if (defaultImplClassName == null) {
- // // non marker interface with no default impl
- // provided
- // reportError("@DeclareParents: used with a non marker interface and no defaultImpl=\"...\" provided",
- // struct);
- // return false;
- // }
hasAtLeastOneMethod = true;
- // What we are saying here:
- // We have this method 'method' and we want to put a
- // forwarding method into a type that matches
- // typePattern that should delegate to the version
- // of the method in 'defaultImplClassName'
-
- // Now the method may be from a supertype but the
- // declaring type of the method we pass into the
- // type
- // munger is what is used to determine the type of
- // the field that hosts the delegate instance.
- // So here we create a modified method with an
- // alternative declaring type so that we lookup
- // the right field. See pr164016.
MethodDelegateTypeMunger mdtm = new MethodDelegateTypeMunger(method, struct.enclosingType, "",
targetTypePattern, struct.method.getName(), struct.method.getSignature());
mdtm.setFieldType(methodReturnType);
}
}
}
- // successfull so far, we thus need a bcel type munger to
- // have
- // a field hosting the mixin in the target type
+ // if any method delegate was created then a field to hold the delegate instance must also be added
if (hasAtLeastOneMethod) {
ResolvedMember fieldHost = AjcMemberMaker.itdAtDeclareParentsField(null, methodReturnType, struct.enclosingType);
struct.ajAttributes.add(new AjAttribute.TypeMunger(new MethodDelegateTypeMunger.FieldHostTypeMunger(fieldHost,
struct.enclosingType, targetTypePattern)));
- } else {
- return false;
}
-
return true;
}