| /* ******************************************************************* |
| * Copyright (c) 2002-2010 Contributors |
| * All rights reserved. |
| * This program and the accompanying materials are made available |
| * under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * ******************************************************************/ |
| package org.aspectj.weaver; |
| |
| import java.io.IOException; |
| import java.lang.reflect.Modifier; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.LinkedHashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.aspectj.bridge.ISourceLocation; |
| |
| /** |
| * Represent a resolved member. Components of it are expected to exist. This member will correspond to a real member *unless* it is |
| * being used to represent the effect of an ITD. |
| * |
| * @author PARC |
| * @author Andy Clement |
| */ |
| public class ResolvedMemberImpl extends MemberImpl implements IHasPosition, ResolvedMember { |
| |
| private String[] parameterNames = null; |
| private boolean isResolved = false; |
| protected UnresolvedType[] checkedExceptions = UnresolvedType.NONE; |
| |
| /** |
| * if this member is a parameterized version of a member in a generic type, then this field holds a reference to the member we |
| * parameterize. |
| */ |
| protected ResolvedMember backingGenericMember = null; |
| |
| protected AnnotationAJ[] annotations = null; |
| protected ResolvedType[] annotationTypes = null; |
| protected AnnotationAJ[][] parameterAnnotations = null; |
| protected ResolvedType[][] parameterAnnotationTypes = null; |
| |
| // Some members are 'created' to represent other things (for example ITDs). |
| // These members have their annotations stored elsewhere, and this flag indicates |
| // that is the case. It is up to the caller to work out where that is! |
| // Once determined the caller may choose to stash the annotations in this |
| // member... |
| private boolean isAnnotatedElsewhere = false; |
| private boolean isAjSynthetic = false; |
| |
| // generic methods have type variables |
| protected TypeVariable[] typeVariables; |
| |
| // these three fields hold the source location of this member |
| protected int start, end; |
| protected ISourceContext sourceContext = null; |
| |
| // XXX deprecate this in favor of the constructor below |
| public ResolvedMemberImpl(MemberKind kind, UnresolvedType declaringType, int modifiers, UnresolvedType returnType, String name, |
| UnresolvedType[] parameterTypes) { |
| super(kind, declaringType, modifiers, returnType, name, parameterTypes); |
| } |
| |
| public ResolvedMemberImpl(MemberKind kind, UnresolvedType declaringType, int modifiers, UnresolvedType returnType, String name, |
| UnresolvedType[] parameterTypes, UnresolvedType[] checkedExceptions) { |
| super(kind, declaringType, modifiers, returnType, name, parameterTypes); |
| this.checkedExceptions = checkedExceptions; |
| } |
| |
| public ResolvedMemberImpl(MemberKind kind, UnresolvedType declaringType, int modifiers, UnresolvedType returnType, String name, |
| UnresolvedType[] parameterTypes, UnresolvedType[] checkedExceptions, ResolvedMember backingGenericMember) { |
| this(kind, declaringType, modifiers, returnType, name, parameterTypes, checkedExceptions); |
| this.backingGenericMember = backingGenericMember; |
| this.isAjSynthetic = backingGenericMember.isAjSynthetic(); |
| } |
| |
| public ResolvedMemberImpl(MemberKind kind, UnresolvedType declaringType, int modifiers, String name, String signature) { |
| super(kind, declaringType, modifiers, name, signature); |
| } |
| |
| /** |
| * Compute the full set of signatures for a member. This walks up the hierarchy giving the ResolvedMember in each defining type |
| * in the hierarchy. A shadowMember can be created with a target type (declaring type) that does not actually define the member. |
| * This is ok as long as the member is inherited in the declaring type. Each declaring type in the line to the actual declaring |
| * type is added as an additional signature. For example: |
| * |
| * class A { void foo(); } class B extends A {} |
| * |
| * shadowMember : void B.foo() |
| * |
| * gives { void B.foo(), void A.foo() } |
| * |
| * @param joinPointSignature |
| * @param inAWorld |
| */ |
| public static JoinPointSignature[] getJoinPointSignatures(Member joinPointSignature, World inAWorld) { |
| |
| // Walk up hierarchy creating one member for each type up to and |
| // including the |
| // first defining type |
| ResolvedType originalDeclaringType = joinPointSignature.getDeclaringType().resolve(inAWorld); |
| ResolvedMemberImpl firstDefiningMember = (ResolvedMemberImpl) joinPointSignature.resolve(inAWorld); |
| if (firstDefiningMember == null) { |
| return JoinPointSignature.EMPTY_ARRAY; |
| } |
| // declaringType can be unresolved if we matched a synthetic member |
| // generated by Aj... |
| // should be fixed elsewhere but add this resolve call on the end for |
| // now so that we can |
| // focus on one problem at a time... |
| ResolvedType firstDefiningType = firstDefiningMember.getDeclaringType().resolve(inAWorld); |
| if (firstDefiningType != originalDeclaringType) { |
| if (joinPointSignature.getKind() == Member.CONSTRUCTOR) { |
| return JoinPointSignature.EMPTY_ARRAY; |
| } |
| // else if (shadowMember.isStatic()) { |
| // return new ResolvedMember[] {firstDefiningMember}; |
| // } |
| } |
| |
| List<ResolvedType> declaringTypes = new ArrayList<ResolvedType>(); |
| accumulateTypesInBetween(originalDeclaringType, firstDefiningType, declaringTypes); |
| Set<ResolvedMember> memberSignatures = new LinkedHashSet<ResolvedMember>(); |
| for (ResolvedType declaringType : declaringTypes) { |
| memberSignatures.add(new JoinPointSignature(firstDefiningMember, declaringType)); |
| } |
| |
| if (shouldWalkUpHierarchyFor(firstDefiningMember)) { |
| // now walk up the hierarchy from the firstDefiningMember and |
| // include the signature for |
| // every type between the firstDefiningMember and the root defining |
| // member. |
| Iterator<ResolvedType> superTypeIterator = firstDefiningType.getDirectSupertypes(); |
| List<ResolvedType> typesAlreadyVisited = new ArrayList<ResolvedType>(); |
| accumulateMembersMatching(firstDefiningMember, superTypeIterator, typesAlreadyVisited, memberSignatures, false); |
| } |
| |
| JoinPointSignature[] ret = new JoinPointSignature[memberSignatures.size()]; |
| memberSignatures.toArray(ret); |
| return ret; |
| } |
| |
| private static boolean shouldWalkUpHierarchyFor(Member aMember) { |
| if (aMember.getKind() == Member.CONSTRUCTOR) { |
| return false; |
| } |
| if (aMember.getKind() == Member.FIELD) { |
| return false; |
| } |
| if (Modifier.isStatic(aMember.getModifiers())) { |
| return false; |
| } |
| return true; |
| } |
| |
| /** |
| * Build a list containing every type between subtype and supertype, inclusively. |
| */ |
| private static void accumulateTypesInBetween(ResolvedType subType, ResolvedType superType, List<ResolvedType> types) { |
| types.add(subType); |
| if (subType == superType) { |
| return; |
| } else { |
| for (Iterator<ResolvedType> iter = subType.getDirectSupertypes(); iter.hasNext();) { |
| ResolvedType parent = iter.next(); |
| if (superType.isAssignableFrom(parent)) { |
| accumulateTypesInBetween(parent, superType, types); |
| } |
| } |
| } |
| } |
| |
| /** |
| * We have a resolved member, possibly with type parameter references as parameters or return type. We need to find all its |
| * ancestor members. When doing this, a type parameter matches regardless of bounds (bounds can be narrowed down the hierarchy). |
| */ |
| private static void accumulateMembersMatching(ResolvedMemberImpl memberToMatch, Iterator<ResolvedType> typesToLookIn, |
| List<ResolvedType> typesAlreadyVisited, Set<ResolvedMember> foundMembers, boolean ignoreGenerics) { |
| while (typesToLookIn.hasNext()) { |
| ResolvedType toLookIn = typesToLookIn.next(); |
| if (!typesAlreadyVisited.contains(toLookIn)) { |
| typesAlreadyVisited.add(toLookIn); |
| ResolvedMemberImpl foundMember = (ResolvedMemberImpl) toLookIn.lookupResolvedMember(memberToMatch, true, |
| ignoreGenerics); |
| if (foundMember != null && isVisibleTo(memberToMatch, foundMember)) { |
| List<ResolvedType> declaringTypes = new ArrayList<ResolvedType>(); |
| // declaring type can be unresolved if the member can from |
| // an ITD... |
| ResolvedType resolvedDeclaringType = foundMember.getDeclaringType().resolve(toLookIn.getWorld()); |
| accumulateTypesInBetween(toLookIn, resolvedDeclaringType, declaringTypes); |
| for (ResolvedType declaringType : declaringTypes) { |
| // typesAlreadyVisited.add(declaringType); |
| foundMembers.add(new JoinPointSignature(foundMember, declaringType)); |
| } |
| if (!ignoreGenerics && toLookIn.isParameterizedType() && (foundMember.backingGenericMember != null)) { |
| foundMembers.add(new JoinPointSignature(foundMember.backingGenericMember, foundMember.declaringType |
| .resolve(toLookIn.getWorld()))); |
| } |
| accumulateMembersMatching(foundMember, toLookIn.getDirectSupertypes(), typesAlreadyVisited, foundMembers, |
| ignoreGenerics); |
| // if this was a parameterized type, look in the generic |
| // type that backs it too |
| } |
| } |
| } |
| } |
| |
| /** |
| * Returns true if the parent member is visible to the child member In the same declaring type this is always true, otherwise if |
| * parent is private it is false. |
| * |
| * @param childMember |
| * @param parentMember |
| * @return |
| */ |
| private static boolean isVisibleTo(ResolvedMember childMember, ResolvedMember parentMember) { |
| if (childMember.getDeclaringType().equals(parentMember.getDeclaringType())) { |
| return true; |
| } |
| if (Modifier.isPrivate(parentMember.getModifiers())) { |
| return false; |
| } else { |
| return true; |
| } |
| } |
| |
| // ---- |
| |
| @Override |
| public final int getModifiers(World world) { |
| return modifiers; |
| } |
| |
| @Override |
| public final int getModifiers() { |
| return modifiers; |
| } |
| |
| // ---- |
| |
| @Override |
| public final UnresolvedType[] getExceptions(World world) { |
| return getExceptions(); |
| } |
| |
| public UnresolvedType[] getExceptions() { |
| return checkedExceptions; |
| } |
| |
| public ShadowMunger getAssociatedShadowMunger() { |
| return null; |
| } |
| |
| // ??? true or false? |
| public boolean isAjSynthetic() { |
| return isAjSynthetic; |
| } |
| |
| protected void setAjSynthetic(boolean b) { |
| isAjSynthetic = b; |
| } |
| |
| public boolean hasAnnotations() { |
| return (annotationTypes != null); |
| } |
| |
| /** |
| * Check if this member has an annotation of the specified type. If the member has a backing generic member then this member |
| * represents a parameterization of a member in a generic type and the annotations available on the backing generic member |
| * should be used. |
| * |
| * @param ofType the type of the annotation being searched for |
| * @return true if the annotation is found on this member or its backing generic member |
| */ |
| public boolean hasAnnotation(UnresolvedType ofType) { |
| // The ctors don't allow annotations to be specified ... yet - but |
| // that doesn't mean it is an error to call this method. |
| // Normally the weaver will be working with subtypes of |
| // this type - BcelField/BcelMethod |
| if (backingGenericMember != null) { |
| if (annotationTypes != null) { |
| throw new BCException("Unexpectedly found a backing generic member and a local set of annotations"); |
| } |
| return backingGenericMember.hasAnnotation(ofType); |
| } |
| if (annotationTypes != null) { |
| for (int i = 0, max = annotationTypes.length; i < max; i++) { |
| if (annotationTypes[i].equals(ofType)) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| public ResolvedType[] getAnnotationTypes() { |
| // The ctors don't allow annotations to be specified ... yet - but |
| // that doesn't mean it is an error to call this method. |
| // Normally the weaver will be working with subtypes of |
| // this type - BcelField/BcelMethod |
| if (backingGenericMember != null) { |
| if (annotationTypes != null) { |
| throw new BCException("Unexpectedly found a backing generic member and a local set of annotations"); |
| } |
| return backingGenericMember.getAnnotationTypes(); |
| } |
| return annotationTypes; |
| } |
| |
| public String getAnnotationDefaultValue() { |
| throw new UnsupportedOperationException( |
| "You should resolve this member and call getAnnotationDefaultValue() on the result..."); |
| } |
| |
| @Override |
| public AnnotationAJ[] getAnnotations() { |
| if (backingGenericMember != null) { |
| return backingGenericMember.getAnnotations(); |
| } |
| if (annotations!=null) { |
| return annotations; |
| } |
| return super.getAnnotations(); |
| } |
| |
| public AnnotationAJ getAnnotationOfType(UnresolvedType ofType) { |
| if (annotations!=null) { |
| // this means they have been set (we are likely a placeholder for an ITD, so a fake member) |
| for (AnnotationAJ annotation: annotations) { |
| if (annotation.getType().equals(ofType)) { |
| return annotation; |
| } |
| } |
| return null; |
| } |
| throw new UnsupportedOperationException("You should resolve this member and call getAnnotationOfType() on the result..."); |
| } |
| |
| public void setAnnotations(AnnotationAJ[] annotations) { |
| this.annotations = annotations; |
| } |
| |
| public void setAnnotationTypes(ResolvedType[] annotationTypes) { |
| this.annotationTypes = annotationTypes; |
| } |
| |
| public ResolvedType[][] getParameterAnnotationTypes() { |
| return parameterAnnotationTypes; |
| } |
| |
| public AnnotationAJ[][] getParameterAnnotations() { |
| if (backingGenericMember != null) { |
| return backingGenericMember.getParameterAnnotations(); |
| } |
| throw new BCException("Cannot return parameter annotations for a " + this.getClass().getName() + " member"); |
| // return super.getParameterAnnotations(); |
| } |
| |
| public void addAnnotation(AnnotationAJ annotation) { |
| if (annotationTypes == null) { |
| annotationTypes = new ResolvedType[1]; |
| annotationTypes[0] = annotation.getType(); |
| annotations = new AnnotationAJ[1]; |
| annotations[0] = annotation; |
| } else { |
| int len = annotations.length; |
| AnnotationAJ[] ret = new AnnotationAJ[len + 1]; |
| System.arraycopy(annotations, 0, ret, 0, len); |
| ret[len] = annotation; |
| annotations = ret; |
| |
| ResolvedType[] newAnnotationTypes = new ResolvedType[len + 1]; |
| System.arraycopy(annotationTypes, 0, newAnnotationTypes, 0, len); |
| newAnnotationTypes[len] = annotation.getType(); |
| annotationTypes = newAnnotationTypes; |
| } |
| } |
| |
| public boolean isBridgeMethod() { |
| return (modifiers & Constants.ACC_BRIDGE) != 0 && getKind().equals(METHOD); |
| } |
| |
| public boolean isVarargsMethod() { |
| return (modifiers & Constants.ACC_VARARGS) != 0; |
| } |
| |
| public void setVarargsMethod() { |
| modifiers = modifiers | Constants.ACC_VARARGS; |
| } |
| |
| public boolean isSynthetic() { |
| // See Bcelmethod.isSynthetic() which takes account of preJava5 |
| // Synthetic modifier |
| return (modifiers & 4096) != 0; // do we know better? |
| } |
| |
| public void write(CompressingDataOutputStream s) throws IOException { |
| getKind().write(s); |
| s.writeBoolean(s.canCompress()); // boolean indicates if parts of this are compressed references |
| |
| // write out the signature of the declaring type of this member |
| if (s.canCompress()) { |
| s.writeCompressedSignature(getDeclaringType().getSignature()); |
| } else { |
| getDeclaringType().write(s); |
| } |
| |
| // write out the modifiers |
| s.writeInt(modifiers); |
| |
| // write out the name and the signature of this member |
| if (s.canCompress()) { |
| s.writeCompressedName(getName()); |
| s.writeCompressedSignature(getSignature()); |
| } else { |
| s.writeUTF(getName()); |
| s.writeUTF(getSignature()); |
| } |
| |
| // write out the array clauses |
| UnresolvedType.writeArray(getExceptions(), s); |
| |
| s.writeInt(getStart()); |
| s.writeInt(getEnd()); |
| s.writeBoolean(isVarargsMethod()); |
| |
| // Write out any type variables... |
| if (typeVariables == null) { |
| s.writeByte(0); |
| } else { |
| s.writeByte(typeVariables.length); |
| for (int i = 0; i < typeVariables.length; i++) { |
| typeVariables[i].write(s); |
| } |
| } |
| String gsig = getGenericSignature(); |
| |
| // change this to a byte: 255=false 0>254 means true and encodes the number of parameters |
| if (getSignature().equals(gsig)) { |
| s.writeByte(0xff); |
| } else { |
| s.writeByte(parameterTypes.length); |
| for (int i = 0; i < parameterTypes.length; i++) { |
| if (s.canCompress()) { |
| s.writeCompressedSignature(parameterTypes[i].getSignature()); |
| } else { |
| UnresolvedType array_element = parameterTypes[i]; |
| array_element.write(s); |
| } |
| } |
| if (s.canCompress()) { |
| s.writeCompressedSignature(returnType.getSignature()); |
| } else { |
| returnType.write(s); |
| } |
| } |
| } |
| |
| /** |
| * Return the member generic signature that would be suitable for inclusion in a class file Signature attribute. For: <T> |
| * List<String> getThem(T t) {} we would create: <T:Ljava/lang/Object;>(TT;)Ljava/util/List<Ljava/lang/String;>;; |
| * |
| * @return the generic signature for the member that could be inserted into a class file |
| */ |
| public String getSignatureForAttribute() { |
| StringBuffer sb = new StringBuffer(); |
| if (typeVariables != null) { |
| sb.append("<"); |
| for (int i = 0; i < typeVariables.length; i++) { |
| sb.append(typeVariables[i].getSignatureForAttribute()); // need |
| // a |
| // 'getSignatureForAttribute()' |
| } |
| sb.append(">"); |
| } |
| sb.append("("); |
| for (int i = 0; i < parameterTypes.length; i++) { |
| ResolvedType ptype = (ResolvedType) parameterTypes[i]; |
| sb.append(ptype.getSignatureForAttribute()); |
| } |
| sb.append(")"); |
| sb.append(((ResolvedType) returnType).getSignatureForAttribute()); |
| return sb.toString(); |
| } |
| |
| public String getGenericSignature() { |
| StringBuffer sb = new StringBuffer(); |
| if (typeVariables != null) { |
| sb.append("<"); |
| for (int i = 0; i < typeVariables.length; i++) { |
| sb.append(typeVariables[i].getSignature()); |
| } |
| sb.append(">"); |
| } |
| sb.append("("); |
| for (int i = 0; i < parameterTypes.length; i++) { |
| UnresolvedType ptype = parameterTypes[i]; |
| sb.append(ptype.getSignature()); |
| } |
| sb.append(")"); |
| sb.append(returnType.getSignature()); |
| return sb.toString(); |
| } |
| |
| public static void writeArray(ResolvedMember[] members, CompressingDataOutputStream s) throws IOException { |
| s.writeInt(members.length); |
| for (int i = 0, len = members.length; i < len; i++) { |
| members[i].write(s); |
| } |
| } |
| |
| public static ResolvedMemberImpl readResolvedMember(VersionedDataInputStream s, ISourceContext sourceContext) |
| throws IOException { |
| |
| MemberKind mk = MemberKind.read(s); |
| boolean compressed = (s.isAtLeast169() ? s.readBoolean() : false); |
| UnresolvedType declaringType = compressed ? UnresolvedType.forSignature(s.readUtf8(s.readShort())) : UnresolvedType.read(s); |
| int modifiers = s.readInt(); |
| String name = compressed ? s.readUtf8(s.readShort()) : s.readUTF(); |
| String signature = compressed ? s.readUtf8(s.readShort()) : s.readUTF(); |
| ResolvedMemberImpl m = new ResolvedMemberImpl(mk, declaringType, modifiers, name, signature); |
| m.checkedExceptions = UnresolvedType.readArray(s); |
| |
| m.start = s.readInt(); |
| m.end = s.readInt(); |
| m.sourceContext = sourceContext; |
| |
| if (s.getMajorVersion() >= AjAttribute.WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ150) { |
| |
| if (s.getMajorVersion() >= AjAttribute.WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ150M4) { |
| boolean isvarargs = s.readBoolean(); |
| if (isvarargs) { |
| m.setVarargsMethod(); |
| } |
| } |
| |
| int tvcount = s.isAtLeast169() ? s.readByte() : s.readInt(); |
| if (tvcount != 0) { |
| m.typeVariables = new TypeVariable[tvcount]; |
| for (int i = 0; i < tvcount; i++) { |
| m.typeVariables[i] = TypeVariable.read(s); |
| m.typeVariables[i].setDeclaringElement(m); |
| m.typeVariables[i].setRank(i); |
| } |
| } |
| if (s.getMajorVersion() >= AjAttribute.WeaverVersionInfo.WEAVER_VERSION_MAJOR_AJ150M4) { |
| int pcount = -1; |
| boolean hasAGenericSignature = false; |
| if (s.isAtLeast169()) { |
| pcount = s.readByte(); |
| hasAGenericSignature = (pcount >= 0 && pcount < 255); |
| } else { |
| hasAGenericSignature = s.readBoolean(); |
| } |
| if (hasAGenericSignature) { |
| int ps = (s.isAtLeast169() ? pcount : s.readInt()); |
| UnresolvedType[] params = new UnresolvedType[ps]; |
| for (int i = 0; i < params.length; i++) { |
| if (compressed) { |
| params[i] = TypeFactory.createTypeFromSignature(s.readSignature()); |
| } else { |
| params[i] = TypeFactory.createTypeFromSignature(s.readUTF()); |
| } |
| } |
| UnresolvedType rt = compressed ? TypeFactory.createTypeFromSignature(s.readSignature()) : TypeFactory |
| .createTypeFromSignature(s.readUTF()); |
| m.parameterTypes = params; |
| m.returnType = rt; |
| } |
| } |
| } |
| return m; |
| } |
| |
| public static ResolvedMember[] readResolvedMemberArray(VersionedDataInputStream s, ISourceContext context) throws IOException { |
| int len = s.readInt(); |
| ResolvedMember[] members = new ResolvedMember[len]; |
| for (int i = 0; i < len; i++) { |
| members[i] = ResolvedMemberImpl.readResolvedMember(s, context); |
| } |
| return members; |
| } |
| |
| // OPTIMIZE dont like how resolve(world) on ResolvedMemberImpl does |
| // something different to world.resolve(member) |
| @Override |
| public ResolvedMember resolve(World world) { |
| if (isResolved) { |
| return this; |
| } |
| // make sure all the pieces of a resolvedmember really are resolved |
| try { |
| if (typeVariables != null && typeVariables.length > 0) { |
| for (int i = 0; i < typeVariables.length; i++) { |
| typeVariables[i] = typeVariables[i].resolve(world); |
| } |
| } |
| world.setTypeVariableLookupScope(this); |
| // if (annotationTypes != null) { |
| // Set<ResolvedType> r = new HashSet<ResolvedType>(); |
| // for (UnresolvedType element : annotationTypes) { |
| // // for (Iterator iter = annotationTypes.iterator(); iter.hasNext();) { |
| // // UnresolvedType element = (UnresolvedType) iter.next(); |
| // r.add(world.resolve(element)); |
| // } |
| // annotationTypes = r; |
| // } |
| declaringType = declaringType.resolve(world); |
| if (declaringType.isRawType()) { |
| declaringType = ((ReferenceType) declaringType).getGenericType(); |
| } |
| |
| if (parameterTypes != null && parameterTypes.length > 0) { |
| for (int i = 0; i < parameterTypes.length; i++) { |
| parameterTypes[i] = parameterTypes[i].resolve(world); |
| } |
| } |
| |
| returnType = returnType.resolve(world); |
| |
| } finally { |
| world.setTypeVariableLookupScope(null); |
| } |
| isResolved = true; |
| return this; |
| } |
| |
| public ISourceContext getSourceContext(World world) { |
| return getDeclaringType().resolve(world).getSourceContext(); |
| } |
| |
| public String[] getParameterNames() { |
| return parameterNames; |
| } |
| |
| public final void setParameterNames(String[] pnames) { |
| parameterNames = pnames; |
| } |
| |
| @Override |
| public final String[] getParameterNames(World world) { |
| return getParameterNames(); |
| } |
| |
| public AjAttribute.EffectiveSignatureAttribute getEffectiveSignature() { |
| return null; |
| } |
| |
| public ISourceLocation getSourceLocation() { |
| // System.out.println("get context: " + this + " is " + sourceContext); |
| if (getSourceContext() == null) { |
| // System.err.println("no context: " + this); |
| return null; |
| } |
| return getSourceContext().makeSourceLocation(this); |
| } |
| |
| public int getEnd() { |
| return end; |
| } |
| |
| public ISourceContext getSourceContext() { |
| return sourceContext; |
| } |
| |
| public int getStart() { |
| return start; |
| } |
| |
| public void setPosition(int sourceStart, int sourceEnd) { |
| this.start = sourceStart; |
| this.end = sourceEnd; |
| } |
| |
| public void setDeclaringType(ReferenceType rt) { |
| declaringType = rt; |
| } |
| |
| public void setSourceContext(ISourceContext sourceContext) { |
| this.sourceContext = sourceContext; |
| } |
| |
| public boolean isAbstract() { |
| return Modifier.isAbstract(modifiers); |
| } |
| |
| public boolean isPublic() { |
| return Modifier.isPublic(modifiers); |
| } |
| |
| public boolean isDefault() { |
| int mods = getModifiers(); |
| return !(Modifier.isPublic(mods) || Modifier.isProtected(mods) || Modifier.isPrivate(mods)); |
| } |
| |
| public boolean isVisible(ResolvedType fromType) { |
| UnresolvedType declaringType = getDeclaringType(); |
| ResolvedType type = null; |
| if (fromType.equals(declaringType)) { |
| type = fromType; |
| } else { |
| World world = fromType.getWorld(); |
| type = declaringType.resolve(world); |
| } |
| return ResolvedType.isVisible(getModifiers(), type, fromType); |
| } |
| |
| public void setCheckedExceptions(UnresolvedType[] checkedExceptions) { |
| this.checkedExceptions = checkedExceptions; |
| } |
| |
| public void setAnnotatedElsewhere(boolean b) { |
| isAnnotatedElsewhere = b; |
| } |
| |
| public boolean isAnnotatedElsewhere() { |
| return isAnnotatedElsewhere; |
| } |
| |
| /** |
| * Get the UnresolvedType for the return type, taking generic signature into account |
| */ |
| @Override |
| public UnresolvedType getGenericReturnType() { |
| return getReturnType(); |
| } |
| |
| /** |
| * Get the TypeXs of the parameter types, taking generic signature into account |
| */ |
| @Override |
| public UnresolvedType[] getGenericParameterTypes() { |
| return getParameterTypes(); |
| } |
| |
| public ResolvedMemberImpl parameterizedWith(UnresolvedType[] typeParameters, ResolvedType newDeclaringType, |
| boolean isParameterized) { |
| return parameterizedWith(typeParameters, newDeclaringType, isParameterized, null); |
| } |
| |
| /** |
| * Return a resolvedmember in which all the type variables in the signature have been replaced with the given bindings. The |
| * 'isParameterized' flag tells us whether we are creating a raw type version or not. if (isParameterized) then List<T> will |
| * turn into List<String> (for example) - if (!isParameterized) then List<T> will turn into List. |
| */ |
| public ResolvedMemberImpl parameterizedWith(UnresolvedType[] typeParameters, ResolvedType newDeclaringType, |
| boolean isParameterized, List<String> aliases) { |
| // PR308773 |
| // this check had problems for the inner type of a generic type because the inner type can be represented |
| // by a 'simple type' if it is only sharing type variables with the outer and has none of its own. To avoid the |
| // check going bang in this case we check for $ (crap...) - we can't check the outer because the declaring type |
| // is considered unresolved... |
| if (// isParameterized && <-- might need this bit... |
| !getDeclaringType().isGenericType() && getDeclaringType().getName().indexOf("$") == -1) { |
| throw new IllegalStateException("Can't ask to parameterize a member of non-generic type: " + getDeclaringType() |
| + " kind(" + getDeclaringType().typeKind + ")"); |
| } |
| TypeVariable[] typeVariables = getDeclaringType().getTypeVariables(); |
| if (isParameterized && (typeVariables.length != typeParameters.length)) { |
| throw new IllegalStateException("Wrong number of type parameters supplied"); |
| } |
| Map<String, UnresolvedType> typeMap = new HashMap<String, UnresolvedType>(); |
| boolean typeParametersSupplied = typeParameters != null && typeParameters.length > 0; |
| if (typeVariables != null) { |
| // If no 'replacements' were supplied in the typeParameters array |
| // then collapse |
| // type variables to their first bound. |
| for (int i = 0; i < typeVariables.length; i++) { |
| UnresolvedType ut = (!typeParametersSupplied ? typeVariables[i].getFirstBound() : typeParameters[i]); |
| typeMap.put(typeVariables[i].getName(), ut); |
| } |
| } |
| // For ITDs on generic types that use type variables from the target type, the aliases |
| // record the alternative names used throughout the ITD expression that must map to |
| // the same value as the type variables real name. |
| if (aliases != null) { |
| int posn = 0; |
| for (String typeVariableAlias : aliases) { |
| typeMap.put(typeVariableAlias, (!typeParametersSupplied ? typeVariables[posn].getFirstBound() |
| : typeParameters[posn])); |
| posn++; |
| } |
| } |
| |
| UnresolvedType parameterizedReturnType = parameterize(getGenericReturnType(), typeMap, isParameterized, |
| newDeclaringType.getWorld()); |
| UnresolvedType[] parameterizedParameterTypes = new UnresolvedType[getGenericParameterTypes().length]; |
| UnresolvedType[] genericParameterTypes = getGenericParameterTypes(); |
| for (int i = 0; i < parameterizedParameterTypes.length; i++) { |
| parameterizedParameterTypes[i] = parameterize(genericParameterTypes[i], typeMap, isParameterized, |
| newDeclaringType.getWorld()); |
| } |
| ResolvedMemberImpl ret = new ResolvedMemberImpl(getKind(), newDeclaringType, getModifiers(), parameterizedReturnType, |
| getName(), parameterizedParameterTypes, getExceptions(), this); |
| ret.setTypeVariables(getTypeVariables()); |
| ret.setSourceContext(getSourceContext()); |
| ret.setPosition(getStart(), getEnd()); |
| ret.setParameterNames(getParameterNames()); |
| return ret; |
| } |
| |
| /** |
| * Replace occurrences of type variables in the signature with values contained in the map. The map is of the form |
| * A=String,B=Integer and so a signature List<A> Foo.m(B i) {} would become List<String> Foo.m(Integer i) {} |
| */ |
| public ResolvedMember parameterizedWith(Map<String, UnresolvedType> m, World w) { |
| // if (//isParameterized && <-- might need this bit... |
| // !getDeclaringType().isGenericType()) { |
| // throw new IllegalStateException( |
| // "Can't ask to parameterize a member of non-generic type: " |
| // +getDeclaringType()+" kind("+ |
| // getDeclaringType().typeKind+")"); |
| // } |
| declaringType = declaringType.resolve(w); |
| if (declaringType.isRawType()) { |
| declaringType = ((ResolvedType) declaringType).getGenericType(); |
| // TypeVariable[] typeVariables = getDeclaringType().getTypeVariables(); |
| // if (isParameterized && (typeVariables.length != |
| // typeParameters.length)) { |
| // throw new |
| // IllegalStateException("Wrong number of type parameters supplied"); |
| // } |
| // Map typeMap = new HashMap(); |
| // boolean typeParametersSupplied = typeParameters!=null && |
| // typeParameters.length>0; |
| // if (typeVariables!=null) { |
| // // If no 'replacements' were supplied in the typeParameters array |
| // then collapse |
| // // type variables to their first bound. |
| // for (int i = 0; i < typeVariables.length; i++) { |
| // UnresolvedType ut = |
| // (!typeParametersSupplied?typeVariables[i].getFirstBound |
| // ():typeParameters[i]); |
| // typeMap.put(typeVariables[i].getName(),ut); |
| // } |
| // } |
| // // For ITDs on generic types that use type variables from the target |
| // type, the aliases |
| // // record the alternative names used throughout the ITD expression |
| // that must map to |
| // // the same value as the type variables real name. |
| // if (aliases!=null) { |
| // int posn = 0; |
| // for (Iterator iter = aliases.iterator(); iter.hasNext();) { |
| // String typeVariableAlias = (String) iter.next(); |
| // typeMap.put(typeVariableAlias,(!typeParametersSupplied?typeVariables[ |
| // posn].getFirstBound():typeParameters[posn])); |
| // posn++; |
| // } |
| // } |
| } |
| |
| UnresolvedType parameterizedReturnType = parameterize(getGenericReturnType(), m, true, w); |
| UnresolvedType[] parameterizedParameterTypes = new UnresolvedType[getGenericParameterTypes().length]; |
| UnresolvedType[] genericParameterTypes = getGenericParameterTypes(); |
| for (int i = 0; i < parameterizedParameterTypes.length; i++) { |
| parameterizedParameterTypes[i] = parameterize(genericParameterTypes[i], m, true, w); |
| } |
| ResolvedMemberImpl ret = new ResolvedMemberImpl(getKind(), declaringType, getModifiers(), parameterizedReturnType, |
| getName(), parameterizedParameterTypes, getExceptions(), this); |
| ret.setTypeVariables(getTypeVariables()); |
| ret.setSourceContext(getSourceContext()); |
| ret.setPosition(getStart(), getEnd()); |
| ret.setParameterNames(getParameterNames()); |
| return ret; |
| } |
| |
| public void setTypeVariables(TypeVariable[] tvars) { |
| typeVariables = tvars; |
| } |
| |
| public TypeVariable[] getTypeVariables() { |
| return typeVariables; |
| } |
| |
| protected UnresolvedType parameterize(UnresolvedType aType, Map<String, UnresolvedType> typeVariableMap, |
| boolean inParameterizedType, World w) { |
| if (aType instanceof TypeVariableReference) { |
| String variableName = ((TypeVariableReference) aType).getTypeVariable().getName(); |
| if (!typeVariableMap.containsKey(variableName)) { |
| return aType; // if the type variable comes from the method (and |
| // not the type) thats OK |
| } |
| return typeVariableMap.get(variableName); |
| } else if (aType.isParameterizedType()) { |
| if (inParameterizedType) { |
| if (w != null) { |
| aType = aType.resolve(w); |
| } else { |
| UnresolvedType dType = getDeclaringType(); |
| aType = aType.resolve(((ResolvedType) dType).getWorld()); |
| } |
| return aType.parameterize(typeVariableMap); |
| } else { |
| return aType.getRawType(); |
| } |
| } else if (aType.isArray()) { |
| // The component type might be a type variable (pr150095) |
| int dims = 1; |
| String sig = aType.getSignature(); |
| // while (sig.charAt(dims) == '[') |
| // dims++; |
| UnresolvedType arrayType = null; |
| UnresolvedType componentSig = UnresolvedType.forSignature(sig.substring(dims)); |
| UnresolvedType parameterizedComponentSig = parameterize(componentSig, typeVariableMap, inParameterizedType, w); |
| if (parameterizedComponentSig.isTypeVariableReference() |
| && parameterizedComponentSig instanceof UnresolvedTypeVariableReferenceType |
| && typeVariableMap.containsKey(((UnresolvedTypeVariableReferenceType) parameterizedComponentSig) |
| .getTypeVariable().getName())) { // pr250632 |
| // TODO ASC bah, this code is rubbish - i should fix it properly |
| StringBuffer newsig = new StringBuffer(); |
| newsig.append("[T"); |
| newsig.append(((UnresolvedTypeVariableReferenceType) parameterizedComponentSig).getTypeVariable().getName()); |
| newsig.append(";"); |
| arrayType = UnresolvedType.forSignature(newsig.toString()); |
| } else { |
| arrayType = ResolvedType.makeArray(parameterizedComponentSig, dims); |
| } |
| return arrayType; |
| } |
| return aType; |
| } |
| |
| /** |
| * If this member is defined by a parameterized super-type, return the erasure of that member. For example: interface I<T> { T |
| * foo(T aTea); } class C implements I<String> { String foo(String aString) { return "something"; } } The resolved member for |
| * C.foo has signature String foo(String). The erasure of that member is Object foo(Object) -- use upper bound of type variable. |
| * A type is a supertype of itself. |
| */ |
| // public ResolvedMember getErasure() { |
| // if (calculatedMyErasure) return myErasure; |
| // calculatedMyErasure = true; |
| // ResolvedType resolvedDeclaringType = (ResolvedType) getDeclaringType(); |
| // // this next test is fast, and the result is cached. |
| // if (!resolvedDeclaringType.hasParameterizedSuperType()) { |
| // return null; |
| // } else { |
| // // we have one or more parameterized super types. |
| // // this member may be defined by one of them... we need to find out. |
| // Collection declaringTypes = |
| // this.getDeclaringTypes(resolvedDeclaringType.getWorld()); |
| // for (Iterator iter = declaringTypes.iterator(); iter.hasNext();) { |
| // ResolvedType aDeclaringType = (ResolvedType) iter.next(); |
| // if (aDeclaringType.isParameterizedType()) { |
| // // we've found the (a?) parameterized type that defines this member. |
| // // now get the erasure of it |
| // ResolvedMemberImpl matchingMember = (ResolvedMemberImpl) |
| // aDeclaringType.lookupMemberNoSupers(this); |
| // if (matchingMember != null && matchingMember.backingGenericMember != |
| // null) { |
| // myErasure = matchingMember.backingGenericMember; |
| // return myErasure; |
| // } |
| // } |
| // } |
| // } |
| // return null; |
| // } |
| // |
| // private ResolvedMember myErasure = null; |
| // private boolean calculatedMyErasure = false; |
| public boolean hasBackingGenericMember() { |
| return backingGenericMember != null; |
| } |
| |
| public ResolvedMember getBackingGenericMember() { |
| return backingGenericMember; |
| } |
| |
| /** |
| * For ITDs, we use the default factory methods to build a resolved member, then alter a couple of characteristics using this |
| * method - this is safe. |
| */ |
| public void resetName(String newName) { |
| this.name = newName; |
| } |
| |
| public void resetKind(MemberKind newKind) { |
| this.kind = newKind; |
| } |
| |
| public void resetModifiers(int newModifiers) { |
| this.modifiers = newModifiers; |
| } |
| |
| public void resetReturnTypeToObjectArray() { |
| returnType = UnresolvedType.OBJECTARRAY; |
| } |
| |
| /** |
| * Returns true if this member matches the other. The matching takes into account name and parameter types only. When comparing |
| * parameter types, we allow any type variable to match any other type variable regardless of bounds. |
| */ |
| public boolean matches(ResolvedMember aCandidateMatch, boolean ignoreGenerics) { |
| ResolvedMemberImpl candidateMatchImpl = (ResolvedMemberImpl) aCandidateMatch; |
| if (!getName().equals(aCandidateMatch.getName())) { |
| return false; |
| } |
| UnresolvedType[] parameterTypes = getGenericParameterTypes(); |
| UnresolvedType[] candidateParameterTypes = aCandidateMatch.getGenericParameterTypes(); |
| if (parameterTypes.length != candidateParameterTypes.length) { |
| return false; |
| } |
| boolean b = false; |
| /* |
| * if (ignoreGenerics) { String myParameterSignature = getParameterSigWithBoundsRemoved(); String |
| * candidateParameterSignature = candidateMatchImpl.getParameterSigWithBoundsRemoved(); if |
| * (myParameterSignature.equals(candidateParameterSignature)) { b = true; } else { myParameterSignature = |
| * (hasBackingGenericMember() ? backingGenericMember.getParameterSignatureErased() : getParameterSignatureErased()); |
| * candidateParameterSignature = (candidateMatchImpl.hasBackingGenericMember() ? candidateMatchImpl.backingGenericMember |
| * .getParameterSignatureErased() : candidateMatchImpl.getParameterSignatureErased()); // System.out.println("my psig = " + |
| * myParameterSignature); // System.out.println("can psig = " + candidateParameterSignature); b = |
| * myParameterSignature.equals(candidateParameterSignature); } } else { |
| */ |
| String myParameterSignature = getParameterSigWithBoundsRemoved(); |
| String candidateParameterSignature = candidateMatchImpl.getParameterSigWithBoundsRemoved(); |
| if (myParameterSignature.equals(candidateParameterSignature)) { |
| b = true; |
| } else { |
| // try erasure |
| myParameterSignature = getParameterSignatureErased(); |
| candidateParameterSignature = candidateMatchImpl.getParameterSignatureErased(); |
| // myParameterSignature = (hasBackingGenericMember() ? backingGenericMember.getParameterSignatureErased() |
| // : getParameterSignatureErased()); |
| // candidateParameterSignature = (candidateMatchImpl.hasBackingGenericMember() ? |
| // candidateMatchImpl.backingGenericMember |
| // .getParameterSignatureErased() : candidateMatchImpl.getParameterSignatureErased()); |
| // System.out.println("my psig = " + myParameterSignature); |
| // System.out.println("can psig = " + candidateParameterSignature); |
| b = myParameterSignature.equals(candidateParameterSignature); |
| // } |
| } |
| // System.out.println("Checking param signatures: " + b); |
| return b; |
| } |
| |
| /** |
| * converts e.g. <T extends Number>.... List<T> to just Ljava/util/List<T;>; whereas the full signature would be |
| * Ljava/util/List<T:Ljava/lang/Number;>; |
| */ |
| private String myParameterSignatureWithBoundsRemoved = null; |
| /** |
| * converts e.g. <T extends Number>.... List<T> to just Ljava/util/List; |
| */ |
| private String myParameterSignatureErasure = null; |
| |
| // does NOT produce a meaningful java signature, but does give a unique |
| // string suitable for |
| // comparison. |
| private String getParameterSigWithBoundsRemoved() { |
| if (myParameterSignatureWithBoundsRemoved != null) { |
| return myParameterSignatureWithBoundsRemoved; |
| } |
| StringBuffer sig = new StringBuffer(); |
| UnresolvedType[] myParameterTypes = getGenericParameterTypes(); |
| for (int i = 0; i < myParameterTypes.length; i++) { |
| appendSigWithTypeVarBoundsRemoved(myParameterTypes[i], sig, new HashSet<UnresolvedType>()); |
| } |
| myParameterSignatureWithBoundsRemoved = sig.toString(); |
| return myParameterSignatureWithBoundsRemoved; |
| } |
| |
| /** |
| * Return the erased form of the signature with bounds collapsed for type variables, etc. Does not include the return type, @see |
| * getParam |
| */ |
| public String getParameterSignatureErased() { |
| if (myParameterSignatureErasure == null) { |
| StringBuilder sig = new StringBuilder(); |
| for (UnresolvedType parameter : getParameterTypes()) { |
| sig.append(parameter.getErasureSignature()); |
| } |
| myParameterSignatureErasure = sig.toString(); |
| } |
| return myParameterSignatureErasure; |
| } |
| |
| public String getSignatureErased() { |
| StringBuffer sb = new StringBuffer(); |
| sb.append("("); |
| sb.append(getParameterSignatureErased()); |
| sb.append(")"); |
| sb.append(getReturnType().getErasureSignature()); |
| return sb.toString(); |
| } |
| |
| // does NOT produce a meaningful java signature, but does give a unique |
| // string suitable for |
| // comparison. |
| public static void appendSigWithTypeVarBoundsRemoved(UnresolvedType aType, StringBuffer toBuffer, |
| Set<UnresolvedType> alreadyUsedTypeVars) { |
| if (aType.isTypeVariableReference()) { |
| TypeVariableReferenceType typeVariableRT = (TypeVariableReferenceType) aType; |
| // pr204505 |
| if (alreadyUsedTypeVars.contains(aType)) { |
| toBuffer.append("..."); |
| } else { |
| alreadyUsedTypeVars.add(aType); |
| appendSigWithTypeVarBoundsRemoved(typeVariableRT.getTypeVariable().getFirstBound(), toBuffer, alreadyUsedTypeVars); |
| } |
| // toBuffer.append("T;"); |
| } else if (aType.isParameterizedType()) { |
| toBuffer.append(aType.getRawType().getSignature()); |
| toBuffer.append("<"); |
| for (int i = 0; i < aType.getTypeParameters().length; i++) { |
| appendSigWithTypeVarBoundsRemoved(aType.getTypeParameters()[i], toBuffer, alreadyUsedTypeVars); |
| } |
| toBuffer.append(">;"); |
| } else { |
| toBuffer.append(aType.getSignature()); |
| } |
| } |
| |
| /** |
| * Useful for writing tests, returns *everything* we know about this member. |
| */ |
| public String toDebugString() { |
| StringBuffer r = new StringBuffer(); |
| |
| // modifiers |
| int mods = modifiers; |
| if ((mods & 4096) > 0) { |
| mods = mods - 4096; // remove synthetic (added in the ASM case but |
| } |
| // not in the BCEL case...) |
| if ((mods & 512) > 0) { |
| mods = mods - 512; // remove interface (added in the BCEL case but |
| } |
| // not in the ASM case...) |
| if ((mods & 131072) > 0) { |
| mods = mods - 131072; // remove deprecated (added in the ASM case |
| } |
| // but not in the BCEL case...) |
| String modsStr = Modifier.toString(mods); |
| if (modsStr.length() != 0) { |
| r.append(modsStr).append("(" + mods + ")").append(" "); |
| } |
| |
| // type variables |
| if (typeVariables != null && typeVariables.length > 0) { |
| r.append("<"); |
| for (int i = 0; i < typeVariables.length; i++) { |
| if (i > 0) { |
| r.append(","); |
| } |
| TypeVariable t = typeVariables[i]; |
| r.append(t.toDebugString()); |
| } |
| r.append("> "); |
| } |
| |
| // 'declaring' type |
| r.append(getGenericReturnType().toDebugString()); |
| r.append(' '); |
| |
| // name |
| r.append(declaringType.getName()); |
| r.append('.'); |
| r.append(name); |
| |
| // parameter signature if a method |
| if (kind != FIELD) { |
| r.append("("); |
| UnresolvedType[] params = getGenericParameterTypes(); |
| boolean parameterNamesExist = showParameterNames && parameterNames != null && parameterNames.length == params.length; |
| if (params.length != 0) { |
| for (int i = 0, len = params.length; i < len; i++) { |
| if (i > 0) { |
| r.append(", "); |
| } |
| r.append(params[i].toDebugString()); |
| if (parameterNamesExist) { |
| r.append(" ").append(parameterNames[i]); |
| } |
| } |
| } |
| r.append(")"); |
| } |
| return r.toString(); |
| } |
| |
| // SECRETAPI - controlling whether parameter names come out in the debug |
| // string (for testing purposes) |
| public static boolean showParameterNames = true; |
| |
| public String toGenericString() { |
| StringBuffer buf = new StringBuffer(); |
| buf.append(getGenericReturnType().getSimpleName()); |
| buf.append(' '); |
| buf.append(declaringType.getName()); |
| buf.append('.'); |
| buf.append(name); |
| if (kind != FIELD) { |
| buf.append("("); |
| UnresolvedType[] params = getGenericParameterTypes(); |
| if (params.length != 0) { |
| buf.append(params[0].getSimpleName()); |
| for (int i = 1, len = params.length; i < len; i++) { |
| buf.append(", "); |
| buf.append(params[i].getSimpleName()); |
| } |
| } |
| buf.append(")"); |
| } |
| return buf.toString(); |
| } |
| |
| public boolean isCompatibleWith(Member am) { |
| if (kind != METHOD || am.getKind() != METHOD) { |
| return true; |
| } |
| if (!name.equals(am.getName())) { |
| return true; |
| } |
| if (!equalTypes(getParameterTypes(), am.getParameterTypes())) { |
| return true; |
| } |
| return getReturnType().equals(am.getReturnType()); |
| } |
| |
| private static boolean equalTypes(UnresolvedType[] a, UnresolvedType[] b) { |
| int len = a.length; |
| if (len != b.length) { |
| return false; |
| } |
| for (int i = 0; i < len; i++) { |
| if (!a[i].equals(b[i])) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| public TypeVariable getTypeVariableNamed(String name) { |
| // Check locally... |
| if (typeVariables != null) { |
| for (int i = 0; i < typeVariables.length; i++) { |
| if (typeVariables[i].getName().equals(name)) { |
| return typeVariables[i]; |
| } |
| } |
| } |
| // check the declaring type! |
| return declaringType.getTypeVariableNamed(name); |
| |
| // Do generic aspects with ITDs that share type variables with the |
| // aspect and the target type and have their own tvars cause |
| // this to be messier? |
| } |
| |
| public void evictWeavingState() { |
| } |
| |
| |
| public boolean isEquivalentTo(Object other) { |
| return this.equals(other); |
| } |
| |
| public boolean isDefaultConstructor() { |
| return false; |
| } |
| } |