package org.aspectj.weaver.patterns;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.aspectj.bridge.IMessage;
import org.aspectj.bridge.ISourceLocation;
import org.aspectj.bridge.Message;
import org.aspectj.weaver.CompressingDataOutputStream;
import org.aspectj.weaver.ISourceContext;
import org.aspectj.weaver.ResolvedType;
import org.aspectj.weaver.UnresolvedType;
import org.aspectj.weaver.VersionedDataInputStream;
import org.aspectj.weaver.WeaverMessages;
import org.aspectj.weaver.World;
public class DeclareParents extends Declare {
protected TypePattern child;
protected TypePatternList parents;
private boolean isWildChild = false;
protected boolean isExtends = true;
public DeclareParents(TypePattern child, List<TypePattern> parents, boolean isExtends) {
this(child, new TypePatternList(parents), isExtends);
}
protected DeclareParents(TypePattern child, TypePatternList parents, boolean isExtends) {
this.child = child;
this.parents = parents;
this.isExtends = isExtends;
WildChildFinder wildChildFinder = new WildChildFinder();
child.accept(wildChildFinder, null);
isWildChild = wildChildFinder.containedWildChild();
}
public boolean match(ResolvedType typeX) {
if (!child.matchesStatically(typeX)) {
return false;
}
if (typeX.getWorld().getLint().typeNotExposedToWeaver.isEnabled() && !typeX.isExposedToWeaver()) {
typeX.getWorld().getLint().typeNotExposedToWeaver.signal(typeX.getName(), getSourceLocation());
}
return true;
}
@Override
public Object accept(PatternNodeVisitor visitor, Object data) {
return visitor.visit(this, data);
}
@Override
public Declare parameterizeWith(Map<String,UnresolvedType> typeVariableBindingMap, World w) {
DeclareParents ret = new DeclareParents(child.parameterizeWith(typeVariableBindingMap, w), parents.parameterizeWith(
typeVariableBindingMap, w), isExtends);
ret.copyLocationFrom(this);
return ret;
}
@Override
public String toString() {
StringBuffer buf = new StringBuffer();
buf.append("declare parents: ");
buf.append(child);
buf.append(isExtends ? " extends " : " implements ");
buf.append(parents);
buf.append(";");
return buf.toString();
}
@Override
public boolean equals(Object other) {
if (!(other instanceof DeclareParents)) {
return false;
}
DeclareParents o = (DeclareParents) other;
return o.child.equals(child) && o.parents.equals(parents);
}
@Override
public int hashCode() {
int result = 23;
result = 37 * result + child.hashCode();
result = 37 * result + parents.hashCode();
return result;
}
@Override
public void write(CompressingDataOutputStream s) throws IOException {
s.writeByte(Declare.PARENTS);
child.write(s);
parents.write(s);
writeLocation(s);
}
public static Declare read(VersionedDataInputStream s, ISourceContext context) throws IOException {
DeclareParents ret = new DeclareParents(TypePattern.read(s, context), TypePatternList.read(s, context), true);
ret.readLocation(context, s);
return ret;
}
public boolean parentsIncludeInterface(World w) {
for (int i = 0; i < parents.size(); i++) {
if (parents.get(i).getExactType().resolve(w).isInterface()) {
return true;
}
}
return false;
}
public boolean parentsIncludeClass(World w) {
for (int i = 0; i < parents.size(); i++) {
if (parents.get(i).getExactType().resolve(w).isClass()) {
return true;
}
}
return false;
}
@Override
public void resolve(IScope scope) {
TypePattern resolvedChild = child.resolveBindings(scope, Bindings.NONE, false, false);
if (!resolvedChild.equals(child)) {
WildChildFinder wildChildFinder = new WildChildFinder();
resolvedChild.accept(wildChildFinder, null);
isWildChild = wildChildFinder.containedWildChild();
this.child = resolvedChild;
}
parents = parents.resolveBindings(scope, Bindings.NONE, false, true);
}
public TypePatternList getParents() {
return parents;
}
public TypePattern getChild() {
return child;
}
public boolean isExtends() {
return this.isExtends;
}
@Override
public boolean isAdviceLike() {
return false;
}
private ResolvedType maybeGetNewParent(ResolvedType targetType, TypePattern typePattern, World world, boolean reportErrors) {
if (typePattern == TypePattern.NO) {
return null;
}
UnresolvedType iType = typePattern.getExactType();
ResolvedType parentType = iType.resolve(world);
if (targetType.equals(world.getCoreType(UnresolvedType.OBJECT))) {
world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.DECP_OBJECT), this.getSourceLocation(), null);
return null;
}
if (parentType.isParameterizedType() || parentType.isRawType()) {
boolean isOK = verifyNoInheritedAlternateParameterization(targetType, parentType, world);
if (!isOK) {
return null;
}
}
if (parentType.isAssignableFrom(targetType)) {
return null;
}
if (reportErrors && isWildChild && targetType.isEnum()) {
world.getLint().enumAsTargetForDecpIgnored.signal(targetType.toString(), getSourceLocation());
}
if (reportErrors && isWildChild && targetType.isAnnotation()) {
world.getLint().annotationAsTargetForDecpIgnored.signal(targetType.toString(), getSourceLocation());
}
if (targetType.isEnum() && parentType.isInterface()) {
if (reportErrors && !isWildChild) {
world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.CANT_DECP_ON_ENUM_TO_IMPL_INTERFACE,
targetType), getSourceLocation(), null);
}
return null;
}
if (targetType.isAnnotation() && parentType.isInterface()) {
if (reportErrors && !isWildChild) {
world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.CANT_DECP_ON_ANNOTATION_TO_IMPL_INTERFACE,
targetType), getSourceLocation(), null);
}
return null;
}
if (targetType.isEnum() && parentType.isClass()) {
if (reportErrors && !isWildChild) {
world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.CANT_DECP_ON_ENUM_TO_EXTEND_CLASS,
targetType), getSourceLocation(), null);
}
return null;
}
if (targetType.isAnnotation() && parentType.isClass()) {
if (reportErrors && !isWildChild) {
world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.CANT_DECP_ON_ANNOTATION_TO_EXTEND_CLASS,
targetType), getSourceLocation(), null);
}
return null;
}
if (parentType.getSignature().equals(UnresolvedType.ENUM.getSignature())) {
if (reportErrors && !isWildChild) {
world.showMessage(IMessage.ERROR, WeaverMessages
.format(WeaverMessages.CANT_DECP_TO_MAKE_ENUM_SUPERTYPE, targetType), getSourceLocation(), null);
}
return null;
}
if (parentType.getSignature().equals(UnresolvedType.ANNOTATION.getSignature())) {
if (reportErrors && !isWildChild) {
world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.CANT_DECP_TO_MAKE_ANNOTATION_SUPERTYPE,
targetType), getSourceLocation(), null);
}
return null;
}
if (parentType.isAssignableFrom(targetType)) {
return null;
}
if (targetType.isAssignableFrom(parentType)) {
world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.CANT_EXTEND_SELF, targetType.getName()), this
.getSourceLocation(), null);
return null;
}
if (parentType.isClass()) {
if (targetType.isInterface()) {
world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.INTERFACE_CANT_EXTEND_CLASS), this
.getSourceLocation(), null);
return null;
}
if (!targetType.getSuperclass().isAssignableFrom(parentType)) {
world.showMessage(IMessage.ERROR, WeaverMessages.format(WeaverMessages.DECP_HIERARCHY_ERROR, iType.getName(),
targetType.getSuperclass().getName()), this.getSourceLocation(), null);
return null;
} else {
return parentType;
}
} else {
return parentType;
}
}
private boolean verifyNoInheritedAlternateParameterization(ResolvedType typeToVerify, ResolvedType newParent, World world) {
if (typeToVerify.equals(ResolvedType.OBJECT)) {
return true;
}
ResolvedType newParentGenericType = newParent.getGenericType();
Iterator<ResolvedType> iter = typeToVerify.getDirectSupertypes();
while (iter.hasNext()) {
ResolvedType supertype = iter.next();
if (((supertype.isRawType() && newParent.isParameterizedType()) || (supertype.isParameterizedType() && newParent
.isRawType()))
&& newParentGenericType.equals(supertype.getGenericType())) {
world.getMessageHandler().handleMessage(
new Message(WeaverMessages.format(WeaverMessages.CANT_DECP_MULTIPLE_PARAMETERIZATIONS, newParent.getName(),
typeToVerify.getName(), supertype.getName()), getSourceLocation(), true,
new ISourceLocation[] { typeToVerify.getSourceLocation() }));
return false;
}
if (supertype.isParameterizedType()) {
ResolvedType generictype = supertype.getGenericType();
if (generictype.isAssignableFrom(newParentGenericType) && !supertype.isAssignableFrom(newParent)) {
world.getMessageHandler().handleMessage(
new Message(WeaverMessages.format(WeaverMessages.CANT_DECP_MULTIPLE_PARAMETERIZATIONS, newParent
.getName(), typeToVerify.getName(), supertype.getName()), getSourceLocation(), true,
new ISourceLocation[] { typeToVerify.getSourceLocation() }));
return false;
}
}
if (!verifyNoInheritedAlternateParameterization(supertype, newParent, world)) {
return false;
}
}
return true;
}
public List<ResolvedType> findMatchingNewParents(ResolvedType onType, boolean reportErrors) {
if (onType.isRawType()) {
onType = onType.getGenericType();
}
if (!match(onType)) {
return Collections.emptyList();
}
List<ResolvedType> ret = new ArrayList<ResolvedType>();
for (int i = 0; i < parents.size(); i++) {
ResolvedType t = maybeGetNewParent(onType, parents.get(i), onType.getWorld(), reportErrors);
if (t != null) {
ret.add(t);
}
}
return ret;
}
@Override
public String getNameSuffix() {
return "parents";
}
public boolean isMixin() {
return false;
}
}