/* *******************************************************************
 * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
 * 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 
 *  
 * Contributors: 
 *     PARC     initial implementation 
 * ******************************************************************/

package org.aspectj.ajdt.internal.compiler.lookup;

import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.aspectj.ajdt.internal.compiler.CommonPrinter;
import org.aspectj.ajdt.internal.compiler.ast.AspectDeclaration;
import org.aspectj.ajdt.internal.compiler.ast.PointcutDeclaration;
import org.aspectj.asm.AsmManager;
import org.aspectj.bridge.IMessage;
import org.aspectj.bridge.WeaveMessage;
import org.aspectj.bridge.context.CompilationAndWeavingContext;
import org.aspectj.bridge.context.ContextToken;
import org.aspectj.org.eclipse.jdt.core.compiler.CharOperation;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.*;
import org.aspectj.org.eclipse.jdt.internal.compiler.env.AccessRestriction;
import org.aspectj.org.eclipse.jdt.internal.compiler.env.IBinaryType;
import org.aspectj.org.eclipse.jdt.internal.compiler.env.INameEnvironment;
import org.aspectj.org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.aspectj.org.eclipse.jdt.internal.compiler.impl.ITypeRequestor;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.*;
import org.aspectj.org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
import org.aspectj.weaver.AnnotationAJ;
import org.aspectj.weaver.ConcreteTypeMunger;
import org.aspectj.weaver.ReferenceType;
import org.aspectj.weaver.ReferenceTypeDelegate;
import org.aspectj.weaver.ResolvedMember;
import org.aspectj.weaver.ResolvedType;
import org.aspectj.weaver.ResolvedTypeMunger;
import org.aspectj.weaver.UnresolvedType;
import org.aspectj.weaver.WeaverMessages;
import org.aspectj.weaver.WeaverStateInfo;
import org.aspectj.weaver.World;
import org.aspectj.weaver.bcel.BcelAnnotation;
import org.aspectj.weaver.bcel.BcelObjectType;
import org.aspectj.weaver.bcel.FakeAnnotation;
import org.aspectj.weaver.bcel.LazyClassGen;
import org.aspectj.weaver.patterns.DeclareAnnotation;
import org.aspectj.weaver.patterns.DeclareParents;

/**
 * Overrides the default eclipse LookupEnvironment for two purposes.
 *
 * 1. To provide some additional phases to <code>completeTypeBindings</code> that weave declare parents and inter-type declarations
 * at the correct time.
 *
 * 2. To intercept the loading of new binary types to ensure the they will have declare parents and inter-type declarations woven
 * when appropriate.
 *
 * @author Jim Hugunin
 */
public class AjLookupEnvironment extends LookupEnvironment implements AnonymousClassCreationListener {
	public EclipseFactory factory = null;

	// private boolean builtInterTypesAndPerClauses = false;
	private final List<SourceTypeBinding> pendingTypesToWeave = new ArrayList<SourceTypeBinding>();

	// Q: What are dangerousInterfaces?
	// A: An interface is considered dangerous if an ITD has been made upon it
	// and that ITD
	// requires the top most implementors of the interface to be woven *and yet*
	// the aspect
	// responsible for the ITD is not in the 'world'.
	// Q: Err, how can that happen?
	// A: When a type is on the inpath, it is 'processed' when completing type
	// bindings. At this
	// point we look at any type mungers it was affected by previously (stored
	// in the weaver
	// state info attribute). Effectively we are working with a type munger and
	// yet may not have its
	// originating aspect in the world. This is a problem if, for example, the
	// aspect supplied
	// a 'body' for a method targetting an interface - since the top most
	// implementors should
	// be woven by the munger from the aspect. When this happens we store the
	// interface name here
	// in the map - if we later process a type that is the topMostImplementor of
	// a dangerous
	// interface then we put out an error message.

	/**
	 * interfaces targetted by ITDs that have to be implemented by accessing the topMostImplementor of the interface, yet the aspect
	 * where the ITD originated is not in the world
	 */
	private final Map dangerousInterfaces = new HashMap();

	public AjLookupEnvironment(ITypeRequestor typeRequestor, CompilerOptions options, ProblemReporter problemReporter,
			INameEnvironment nameEnvironment) {
		super(typeRequestor, options, problemReporter, nameEnvironment);
	}

	// ??? duplicates some of super's code
	public void completeTypeBindings() {
		AsmManager.setCompletingTypeBindings(true);
		ContextToken completeTypeBindingsToken = CompilationAndWeavingContext.enteringPhase(
				CompilationAndWeavingContext.COMPLETING_TYPE_BINDINGS, "");
		// builtInterTypesAndPerClauses = false;
		// pendingTypesToWeave = new ArrayList();
		stepCompleted = BUILD_TYPE_HIERARCHY;

		for (int i = lastCompletedUnitIndex + 1; i <= lastUnitIndex; i++) {
			ContextToken tok = CompilationAndWeavingContext.enteringPhase(CompilationAndWeavingContext.CHECK_AND_SET_IMPORTS,
					units[i].compilationResult.fileName);
			units[i].scope.checkAndSetImports();
			CompilationAndWeavingContext.leavingPhase(tok);
		}
		stepCompleted = CHECK_AND_SET_IMPORTS;

		for (int i = lastCompletedUnitIndex + 1; i <= lastUnitIndex; i++) {
			ContextToken tok = CompilationAndWeavingContext.enteringPhase(CompilationAndWeavingContext.CONNECTING_TYPE_HIERARCHY,
					units[i].compilationResult.fileName);
			units[i].scope.connectTypeHierarchy();
			CompilationAndWeavingContext.leavingPhase(tok);
		}
		stepCompleted = CONNECT_TYPE_HIERARCHY;

		for (int i = lastCompletedUnitIndex + 1; i <= lastUnitIndex; i++) {
			ContextToken tok = CompilationAndWeavingContext.enteringPhase(CompilationAndWeavingContext.BUILDING_FIELDS_AND_METHODS,
					units[i].compilationResult.fileName);
			// units[i].scope.checkParameterizedTypes(); do this check a little
			// later, after ITDs applied to stbs
			units[i].scope.buildFieldsAndMethods();
			CompilationAndWeavingContext.leavingPhase(tok);
		}

		// would like to gather up all TypeDeclarations at this point and put
		// them in the factory
		for (int i = lastCompletedUnitIndex + 1; i <= lastUnitIndex; i++) {
			SourceTypeBinding[] b = units[i].scope.topLevelTypes;
			for (int j = 0; j < b.length; j++) {
				factory.addSourceTypeBinding(b[j], units[i]);
				if (b[j].superclass instanceof MissingTypeBinding) {
					// e37: Undoing the work in ClassScope.connectSuperClass() as it will lead to cascade errors
					// TODO allow MissingTypeBinding through here and cope with it in all situations later?
					b[j].superclass = units[i].scope.getJavaLangObject();
				}
			}
		}

		// We won't find out about anonymous types until later though, so
		// register to be
		// told about them when they turn up.
		AnonymousClassPublisher.aspectOf().setAnonymousClassCreationListener(this);

		// need to build inter-type declarations for all AspectDeclarations at
		// this point
		// this MUST be done in order from super-types to subtypes
		List<SourceTypeBinding> typesToProcess = new ArrayList<SourceTypeBinding>();
		List<SourceTypeBinding> aspectsToProcess = new ArrayList<SourceTypeBinding>();
		for (int i = lastCompletedUnitIndex + 1; i <= lastUnitIndex; i++) {
			CompilationUnitScope cus = units[i].scope;
			SourceTypeBinding[] stbs = cus.topLevelTypes;
			for (int j = 0; j < stbs.length; j++) {
				SourceTypeBinding stb = stbs[j];
				typesToProcess.add(stb);
				TypeDeclaration typeDeclaration = stb.scope.referenceContext;
				if (typeDeclaration instanceof AspectDeclaration) {
					aspectsToProcess.add(stb);
				}
			}
		}
		factory.getWorld().getCrosscuttingMembersSet().reset();

		// Need to do these before the other ITDs
		for (SourceTypeBinding aspectToProcess : aspectsToProcess) {
			processInterTypeMemberTypes(aspectToProcess.scope);
		}

		while (typesToProcess.size() > 0) {
			// removes types from the list as they are processed...
			collectAllITDsAndDeclares(typesToProcess.get(0), typesToProcess);
		}

		factory.finishTypeMungers();

		// now do weaving
		final List<ConcreteTypeMunger> typeMungers = factory.getTypeMungers();

		final List<DeclareParents> declareParents = factory.getDeclareParents();
		final List<DeclareAnnotation> declareAnnotationOnTypes = factory.getDeclareAnnotationOnTypes();

		doPendingWeaves();

		// We now have some list of types to process, and we are about to apply
		// the type mungers.
		// There can be situations where the order of types passed to the
		// compiler causes the
		// output from the compiler to vary - THIS IS BAD. For example, if we
		// have class A
		// and class B extends A. Also, an aspect that 'declare parents: A+
		// implements Serializable'
		// then depending on whether we see A first, we may or may not make B
		// serializable.

		// The fix is to process them in the right order, ensuring that for a
		// type we process its
		// supertypes and superinterfaces first. This algorithm may have
		// problems with:
		// - partial hierarchies (e.g. suppose types A,B,C are in a hierarchy
		// and A and C are to be woven but not B)
		// - weaving that brings new types in for processing (see
		// pendingTypesToWeave.add() calls) after we thought
		// we had the full list.
		//
		// but these aren't common cases (he bravely said...)
		boolean typeProcessingOrderIsImportant = declareParents.size() > 0 || declareAnnotationOnTypes.size() > 0; // DECAT

		if (typeProcessingOrderIsImportant) {
			typesToProcess = new ArrayList<SourceTypeBinding>();
			for (int i = lastCompletedUnitIndex + 1; i <= lastUnitIndex; i++) {
				CompilationUnitScope cus = units[i].scope;
				SourceTypeBinding[] stbs = cus.topLevelTypes;
				for (int j = 0; j < stbs.length; j++) {
					SourceTypeBinding stb = stbs[j];
					typesToProcess.add(stb);
				}
			}

			List<SourceTypeBinding> stb2 = new ArrayList<SourceTypeBinding>();
			stb2.addAll(typesToProcess);

			while (typesToProcess.size() > 0) {
				// A side effect of weaveIntertypes() is that the processed type is removed from the collection
				weaveIntertypes(typesToProcess, typesToProcess.get(0), typeMungers, declareParents, declareAnnotationOnTypes, 1);
			}

			while (stb2.size() > 0) {
				// A side effect of weaveIntertypes() is that the processed type is removed from the collection
				weaveIntertypes(stb2, stb2.get(0), typeMungers, declareParents, declareAnnotationOnTypes, 2);
			}

		} else {
			// Order isn't important
			for (int i = lastCompletedUnitIndex + 1; i <= lastUnitIndex; i++) {
				weaveInterTypeDeclarations(units[i].scope, typeMungers, declareParents, declareAnnotationOnTypes);
			}
		}

		for (int i = lastCompletedUnitIndex + 1; i <= lastUnitIndex; i++) {
			units[i].scope.checkParameterizedTypes();
		}

		for (int i = lastCompletedUnitIndex + 1; i <= lastUnitIndex; i++) {
			SourceTypeBinding[] b = units[i].scope.topLevelTypes;
			for (int j = 0; j < b.length; j++) {
				ContextToken tok = CompilationAndWeavingContext.enteringPhase(
						CompilationAndWeavingContext.RESOLVING_POINTCUT_DECLARATIONS, b[j].sourceName);
				resolvePointcutDeclarations(b[j].scope);
				CompilationAndWeavingContext.leavingPhase(tok);
			}
		}

		for (int i = lastCompletedUnitIndex + 1; i <= lastUnitIndex; i++) {
			SourceTypeBinding[] b = units[i].scope.topLevelTypes;
			for (int j = 0; j < b.length; j++) {
				ContextToken tok = CompilationAndWeavingContext.enteringPhase(
						CompilationAndWeavingContext.ADDING_DECLARE_WARNINGS_AND_ERRORS, b[j].sourceName);
				addAdviceLikeDeclares(b[j].scope);
				CompilationAndWeavingContext.leavingPhase(tok);
			}
		}

		for (int i = lastCompletedUnitIndex + 1; i <= lastUnitIndex; i++) {
			units[i] = null; // release unnecessary reference to the parsed unit
		}

		stepCompleted = BUILD_FIELDS_AND_METHODS;
		lastCompletedUnitIndex = lastUnitIndex;
		AsmManager.setCompletingTypeBindings(false);
		factory.getWorld().getCrosscuttingMembersSet().verify();
		CompilationAndWeavingContext.leavingPhase(completeTypeBindingsToken);

		if (isProcessingAnnotations) {
			throw new SourceTypeCollisionException(); // TODO(yushkovskiy): temporary solution; forcing to recompile units to insert mungers into types
		}
	}

	// /**
	// * For any given sourcetypebinding, this method checks that if it is a
	// parameterized aspect that
	// * the type parameters specified for any supertypes meet the bounds for
	// the generic type
	// * variables.
	// */
	// private void verifyAnyTypeParametersMeetBounds(SourceTypeBinding
	// sourceType) {
	// ResolvedType onType = factory.fromEclipse(sourceType);
	// if (onType.isAspect()) {
	// ResolvedType superType = factory.fromEclipse(sourceType.superclass);
	// // Don't need to check if it was used in its RAW form or isnt generic
	// if (superType.isGenericType() || superType.isParameterizedType()) {
	// TypeVariable[] typeVariables = superType.getTypeVariables();
	// UnresolvedType[] typeParams = superType.getTypeParameters();
	// if (typeVariables!=null && typeParams!=null) {
	// for (int i = 0; i < typeVariables.length; i++) {
	// boolean ok =
	// typeVariables[i].canBeBoundTo(typeParams[i].resolve(factory.getWorld()));
	// if (!ok) { // the supplied parameter violates the bounds
	// // Type {0} does not meet the specification for type parameter {1} ({2})
	// in generic type {3}
	// String msg =
	// WeaverMessages.format(
	// WeaverMessages.VIOLATES_TYPE_VARIABLE_BOUNDS,
	// typeParams[i],
	// new Integer(i+1),
	// typeVariables[i].getDisplayName(),
	// superType.getGenericType().getName());
	// factory.getWorld().getMessageHandler().handleMessage(MessageUtil.error(msg
	// ,onType.getSourceLocation()));
	// }
	// }
	// }
	// }
	// }
	// }

	public void doSupertypesFirst(ReferenceBinding rb, Collection<? extends ReferenceBinding> yetToProcess) {
		if (rb instanceof SourceTypeBinding) {
			if (yetToProcess.contains(rb)) {
				collectAllITDsAndDeclares((SourceTypeBinding) rb, yetToProcess);
			}
		} else if (rb instanceof ParameterizedTypeBinding) {
			// If its a PTB we need to pull the SourceTypeBinding out of it.
			ParameterizedTypeBinding ptb = (ParameterizedTypeBinding) rb;
			if (ptb.type instanceof SourceTypeBinding && yetToProcess.contains(ptb.type)) {
				collectAllITDsAndDeclares((SourceTypeBinding) ptb.type, yetToProcess);
			}
		}
	}

	/**
	 * Find all the ITDs and Declares, but it is important we do this from the supertypes down to the subtypes.
	 *
	 * @param sourceType
	 * @param yetToProcess
	 */
	private void collectAllITDsAndDeclares(SourceTypeBinding sourceType, Collection<? extends ReferenceBinding> yetToProcess) {
		// Look at the supertype first
		ContextToken tok = CompilationAndWeavingContext.enteringPhase(CompilationAndWeavingContext.COLLECTING_ITDS_AND_DECLARES,
				sourceType.sourceName);

		yetToProcess.remove(sourceType);
		// look out our direct supertype
		doSupertypesFirst(sourceType.superclass(), yetToProcess);

		// now check our membertypes (pr119570)
		ReferenceBinding[] memberTypes = sourceType.memberTypes;
		for (int i = 0, length = memberTypes.length; i < length; i++) {
			SourceTypeBinding rb = (SourceTypeBinding) memberTypes[i];
			if (!rb.superclass().equals(sourceType)) {
				doSupertypesFirst(rb.superclass(), yetToProcess);
			}
		}

		buildInterTypeAndPerClause(sourceType.scope);
		addCrosscuttingStructures(sourceType.scope);
		CompilationAndWeavingContext.leavingPhase(tok);
	}

	/**
	 * Weave the parents and intertype decls into a given type. This method looks at the supertype and superinterfaces for the
	 * specified type and recurses to weave those first if they are in the full list of types we are going to process during this
	 * compile... it stops recursing the first time it hits a type we aren't going to process during this compile. This could cause
	 * problems if you supply 'pieces' of a hierarchy, i.e. the bottom and the top, but not the middle - but what the hell are you
	 * doing if you do that?
	 *
	 * @param mode 0=do everything, 1=do declare parents, 2=do ITDs
	 */
	private void weaveIntertypes(List<SourceTypeBinding> typesToProcess, SourceTypeBinding typeToWeave,
			List<ConcreteTypeMunger> typeMungers, List<DeclareParents> declareParents,
			List<DeclareAnnotation> declareAnnotationOnTypes, int mode) {
		// Look at the supertype first
		ReferenceBinding superType = typeToWeave.superclass();
		if (typesToProcess.contains(superType) && superType instanceof SourceTypeBinding) {
			// System.err.println("Recursing to supertype "+new
			// String(superType.getFileName()));
			weaveIntertypes(typesToProcess, (SourceTypeBinding) superType, typeMungers, declareParents, declareAnnotationOnTypes,
					mode);
		}
		// Then look at the superinterface list
		ReferenceBinding[] interfaceTypes = typeToWeave.superInterfaces();
		for (int i = 0; i < interfaceTypes.length; i++) {
			ReferenceBinding binding = interfaceTypes[i];
			if (typesToProcess.contains(binding) && binding instanceof SourceTypeBinding) {
				// System.err.println("Recursing to superinterface "+new
				// String(binding.getFileName()));
				weaveIntertypes(typesToProcess, (SourceTypeBinding) binding, typeMungers, declareParents, declareAnnotationOnTypes,
						mode);
			}
			else if (binding instanceof ParameterizedTypeBinding && (((ParameterizedTypeBinding)binding).type instanceof SourceTypeBinding) && typesToProcess.contains(((ParameterizedTypeBinding)binding).type)) {
				weaveIntertypes(typesToProcess, (SourceTypeBinding) ((ParameterizedTypeBinding)binding).type, typeMungers, declareParents, declareAnnotationOnTypes, mode);
			}
		}
		weaveInterTypeDeclarations(typeToWeave, typeMungers, declareParents, declareAnnotationOnTypes, false, mode);
		typesToProcess.remove(typeToWeave);
	}

	private void doPendingWeaves() {
		for (SourceTypeBinding t: pendingTypesToWeave) {
			ContextToken tok = CompilationAndWeavingContext.enteringPhase(
					CompilationAndWeavingContext.WEAVING_INTERTYPE_DECLARATIONS, t.sourceName);
			weaveInterTypeDeclarations(t);
			CompilationAndWeavingContext.leavingPhase(tok);
		}
		pendingTypesToWeave.clear();
	}

	private void addAdviceLikeDeclares(ClassScope s) {
		TypeDeclaration dec = s.referenceContext;

		if (dec instanceof AspectDeclaration) {
			ResolvedType typeX = factory.fromEclipse(dec.binding);
			factory.getWorld().getCrosscuttingMembersSet().addAdviceLikeDeclares(typeX);
		}

		SourceTypeBinding sourceType = s.referenceContext.binding;
		ReferenceBinding[] memberTypes = sourceType.memberTypes;
		for (int i = 0, length = memberTypes.length; i < length; i++) {
			addAdviceLikeDeclares(((SourceTypeBinding) memberTypes[i]).scope);
		}
	}

	private void addCrosscuttingStructures(ClassScope s) {
		TypeDeclaration dec = s.referenceContext;

		if (dec instanceof AspectDeclaration) {
			ResolvedType typeX = factory.fromEclipse(dec.binding);
			factory.getWorld().getCrosscuttingMembersSet().addOrReplaceAspect(typeX, false);

			if (typeX.getSuperclass().isAspect() && !typeX.getSuperclass().isExposedToWeaver()) {
				factory.getWorld().getCrosscuttingMembersSet().addOrReplaceAspect(typeX.getSuperclass(), false);
			}
		}

		SourceTypeBinding sourceType = s.referenceContext.binding;
		ReferenceBinding[] memberTypes = sourceType.memberTypes;
		for (int i = 0, length = memberTypes.length; i < length; i++) {
			addCrosscuttingStructures(((SourceTypeBinding) memberTypes[i]).scope);
		}
	}

	private void resolvePointcutDeclarations(ClassScope s) {
		TypeDeclaration dec = s.referenceContext;
		SourceTypeBinding sourceType = s.referenceContext.binding;
		boolean hasPointcuts = false;
		AbstractMethodDeclaration[] methods = dec.methods;
		boolean initializedMethods = false;
		if (methods != null) {
			for (int i = 0; i < methods.length; i++) {
				if (methods[i] instanceof PointcutDeclaration) {
					hasPointcuts = true;
					if (!initializedMethods) {
						sourceType.methods(); // force initialization
						initializedMethods = true;
					}
					((PointcutDeclaration) methods[i]).resolvePointcut(s);
				}
			}
		}

		if (hasPointcuts || dec instanceof AspectDeclaration || couldBeAnnotationStyleAspectDeclaration(dec)) {
			ReferenceType name = (ReferenceType) factory.fromEclipse(sourceType);
			EclipseSourceType eclipseSourceType = (EclipseSourceType) name.getDelegate();
			eclipseSourceType.checkPointcutDeclarations();
		}

		ReferenceBinding[] memberTypes = sourceType.memberTypes;
		for (int i = 0, length = memberTypes.length; i < length; i++) {
			resolvePointcutDeclarations(((SourceTypeBinding) memberTypes[i]).scope);
		}
	}

	/**
	 * Return true if the declaration has @Aspect annotation. Called 'couldBe' rather than 'is' because someone else may have
	 * defined an annotation called Aspect - we can't verify the full name (including package name) because it may not have been
	 * resolved just yet and rather going through expensive resolution when we dont have to, this gives us a cheap check that tells
	 * us whether to bother.
	 */
	private boolean couldBeAnnotationStyleAspectDeclaration(TypeDeclaration dec) {
		Annotation[] annotations = dec.annotations;
		boolean couldBeAtAspect = false;
		if (annotations != null) {
			for (int i = 0; i < annotations.length && !couldBeAtAspect; i++) {
				if (annotations[i].toString().equals("@Aspect")) {
					couldBeAtAspect = true;
				}
			}
		}
		return couldBeAtAspect;
	}

	/**
	 * Applies any intertype member type declarations up front.
	 */
	private void processInterTypeMemberTypes(ClassScope classScope) {
		TypeDeclaration dec = classScope.referenceContext;
		if (dec instanceof AspectDeclaration) {
			((AspectDeclaration) dec).processIntertypeMemberTypes(classScope);
		}
		// if we are going to support nested aspects making itd member types, copy the logic from the end of
		// buildInterTypeAndPerClause() which walks members
	}

	private void buildInterTypeAndPerClause(ClassScope s) {
		TypeDeclaration dec = s.referenceContext;
		if (dec instanceof AspectDeclaration) {
			((AspectDeclaration) dec).buildInterTypeAndPerClause(s);
		}

		SourceTypeBinding sourceType = s.referenceContext.binding;
		// test classes don't extend aspects
		if (sourceType.superclass != null) {
			ResolvedType parent = factory.fromEclipse(sourceType.superclass);
			if (parent.isAspect() && !isAspect(dec)) {
				factory.showMessage(IMessage.ERROR, "class \'" + new String(sourceType.sourceName) + "\' can not extend aspect \'"
						+ parent.getName() + "\'", factory.fromEclipse(sourceType).getSourceLocation(), null);
			}
		}

		ReferenceBinding[] memberTypes = sourceType.memberTypes;
		if (memberTypes == null) {
			System.err.println("Unexpectedly found null for memberTypes of " + sourceType.debugName());
		}
		if (memberTypes != null) {
			for (int i = 0, length = memberTypes.length; i < length; i++) {
				buildInterTypeAndPerClause(((SourceTypeBinding) memberTypes[i]).scope);
			}
		}
	}

	private boolean isAspect(TypeDeclaration decl) {
		if ((decl instanceof AspectDeclaration)) {
			return true;
		} else if (decl.annotations == null) {
			return false;
		} else {
			for (int i = 0; i < decl.annotations.length; i++) {
				Annotation ann = decl.annotations[i];
				if (ann.type instanceof SingleTypeReference) {
					if (CharOperation.equals("Aspect".toCharArray(), ((SingleTypeReference) ann.type).token)) {
						return true;
					}
				} else if (ann.type instanceof QualifiedTypeReference) {
					QualifiedTypeReference qtr = (QualifiedTypeReference) ann.type;
					if (qtr.tokens.length != 5) {
						return false;
					}
					if (!CharOperation.equals("org".toCharArray(), qtr.tokens[0])) {
						return false;
					}
					if (!CharOperation.equals("aspectj".toCharArray(), qtr.tokens[1])) {
						return false;
					}
					if (!CharOperation.equals("lang".toCharArray(), qtr.tokens[2])) {
						return false;
					}
					if (!CharOperation.equals("annotation".toCharArray(), qtr.tokens[3])) {
						return false;
					}
					if (!CharOperation.equals("Aspect".toCharArray(), qtr.tokens[4])) {
						return false;
					}
					return true;
				}
			}
		}
		return false;
	}

	private void weaveInterTypeDeclarations(CompilationUnitScope unit, List<ConcreteTypeMunger> typeMungers,
			List<DeclareParents> declareParents, List<DeclareAnnotation> declareAnnotationOnTypes) {
		for (int i = 0, length = unit.topLevelTypes.length; i < length; i++) {
			weaveInterTypeDeclarations(unit.topLevelTypes[i], typeMungers, declareParents, declareAnnotationOnTypes, false, 0);
		}
	}

	private void weaveInterTypeDeclarations(SourceTypeBinding sourceType) {
		if (!factory.areTypeMungersFinished()) {
			if (!pendingTypesToWeave.contains(sourceType)) {
				pendingTypesToWeave.add(sourceType);

				// inner type ITD support - may need this for some incremental cases...
				// List<ConcreteTypeMunger> ctms = factory.getWorld().getCrosscuttingMembersSet().getTypeMungersOfKind(
				// ResolvedTypeMunger.InnerClass);
				// // List<ConcreteTypeMunger> innerTypeMungers = new ArrayList<ConcreteTypeMunger>();
				// // for (ConcreteTypeMunger ctm : ctms) {
				// // if (ctm.getMunger() != null && ctm.getMunger().getKind() == ResolvedTypeMunger.InnerClass) {
				// // innerTypeMungers.add(ctm);
				// // }
				// // }
				// // that includes the innertype one...
				// // doPendingWeaves at this level is about applying inner class
				// BinaryTypeBinding t = (BinaryTypeBinding) sourceType;
				// for (ConcreteTypeMunger ctm : innerTypeMungers) {
				// NewMemberClassTypeMunger nmctm = (NewMemberClassTypeMunger) ctm.getMunger();
				// ReferenceBinding[] rbs = t.memberTypes;
				// UnresolvedType ut = factory.fromBinding(t);
				// if (ut.equals(nmctm.getTargetType())) {
				// // got a match here
				// SourceTypeBinding aspectTypeBinding = (SourceTypeBinding) factory.makeTypeBinding(ctm.getAspectType());
				//
				// char[] mungerMemberTypeName = ("$" + nmctm.getMemberTypeName()).toCharArray();
				// ReferenceBinding innerTypeBinding = null;
				// for (ReferenceBinding innerType : aspectTypeBinding.memberTypes) {
				// char[] compounded = CharOperation.concatWith(innerType.compoundName, '.');
				// if (org.aspectj.org.eclipse.jdt.core.compiler.CharOperation.endsWith(compounded, mungerMemberTypeName)) {
				// innerTypeBinding = innerType;
				// break;
				// }
				// }
				// // may be unresolved if the aspect type binding was a BinaryTypeBinding
				// if (innerTypeBinding instanceof UnresolvedReferenceBinding) {
				// innerTypeBinding = BinaryTypeBinding
				// .resolveType(innerTypeBinding, factory.getLookupEnvironment(), true);
				// }
				// t.memberTypes(); // cause initialization
				// t.memberTypes = new ReferenceBinding[] { innerTypeBinding };
				//
				// int stop = 1;
				// // The inner type from the aspect should be put into the membertypebindings for this
				//
				// }
				// }

			}
		} else {
			weaveInterTypeDeclarations(sourceType, factory.getTypeMungers(), factory.getDeclareParents(),
					factory.getDeclareAnnotationOnTypes(), true, 0);
		}
	}

	/**
	 * @param mode 0=do everything, 1=do declare parents, 2=do ITDs
	 */
	private void weaveInterTypeDeclarations(SourceTypeBinding sourceType, List<ConcreteTypeMunger> typeMungers,
			List<DeclareParents> declareParents, List<DeclareAnnotation> declareAnnotationOnTypes, boolean skipInners, int mode) {

		ContextToken tok = CompilationAndWeavingContext.enteringPhase(CompilationAndWeavingContext.WEAVING_INTERTYPE_DECLARATIONS,
				sourceType.sourceName);

		ResolvedType onType = factory.fromEclipse(sourceType);

		// AMC we shouldn't need this when generic sigs are fixed??
		if (onType.isRawType()) {
			onType = onType.getGenericType();
		}

		WeaverStateInfo info = onType.getWeaverState();

		if (mode < 2) {
			// this test isnt quite right - there will be a case where we fail to
			// flag a problem
			// with a 'dangerous interface' because the type is reweavable when we
			// should have
			// because the type wasn't going to be rewoven... if that happens, we
			// should perhaps
			// move this test and dangerous interface processing to the end of this
			// method and
			// make it conditional on whether any of the typeMungers passed into
			// here actually
			// matched this type.
			if (info != null && !info.isOldStyle() && !info.isReweavable()) {
				processTypeMungersFromExistingWeaverState(sourceType, onType);
				CompilationAndWeavingContext.leavingPhase(tok);
				return;
			}

			// Check if the type we are looking at is the topMostImplementor of a
			// dangerous interface -
			// report a problem if it is.
			for (Iterator i = dangerousInterfaces.entrySet().iterator(); i.hasNext();) {
				Map.Entry entry = (Map.Entry) i.next();
				ResolvedType interfaceType = (ResolvedType) entry.getKey();
				if (onType.isTopmostImplementor(interfaceType)) {
					factory.showMessage(IMessage.ERROR, onType + ": " + entry.getValue(), onType.getSourceLocation(), null);
				}
			}

			boolean needOldStyleWarning = (info != null && info.isOldStyle());

			onType.clearInterTypeMungers();
			onType.ensureConsistent();

			// FIXME asc perf Could optimize here, after processing the expected set
			// of types we may bring
			// binary types that are not exposed to the weaver, there is no need to
			// attempt declare parents
			// or declare annotation really - unless we want to report the
			// not-exposed to weaver
			// messages...

			List<DeclareParents> decpToRepeat = new ArrayList<DeclareParents>();
			List<DeclareAnnotation> decaToRepeat = new ArrayList<DeclareAnnotation>();
			boolean anyNewParents = false;
			boolean anyNewAnnotations = false;

			// first pass
			// try and apply all decps - if they match, then great. If they don't
			// then
			// check if they are starred-annotation patterns. If they are not
			// starred
			// annotation patterns then they might match later...remember that...
			for (DeclareParents decp : declareParents) {
				if (!decp.isMixin()) {
					boolean didSomething = doDeclareParents(decp, sourceType);
					if (didSomething) {
						if (factory.pushinCollector != null) {
							factory.pushinCollector.tagAsMunged(sourceType, decp.getParents().get(0));
						}
						anyNewParents = true;
					} else {
						if (!decp.getChild().isStarAnnotation()) {
							decpToRepeat.add(decp);
						}
					}
				}
			}

			for (DeclareAnnotation deca : declareAnnotationOnTypes) {
				boolean didSomething = doDeclareAnnotations(deca, sourceType, true);
				if (didSomething) {
					anyNewAnnotations = true;
				} else {
					if (!deca.getTypePattern().isStar()) {
						decaToRepeat.add(deca);
					}
				}
			}

			List<Object> forRemoval = new ArrayList<Object>();
			// now lets loop over and over until we have done all we can
			while ((anyNewAnnotations || anyNewParents) && (!decpToRepeat.isEmpty() || !decaToRepeat.isEmpty())) {
				anyNewParents = anyNewAnnotations = false;
				forRemoval.clear();
				for (DeclareParents decp : decpToRepeat) {
					boolean didSomething = doDeclareParents(decp, sourceType);
					if (didSomething) {
						if (factory.pushinCollector != null) {
							factory.pushinCollector.tagAsMunged(sourceType, decp.getParents().get(0));
						}
						anyNewParents = true;
						forRemoval.add(decp);
					}
				}
				decpToRepeat.removeAll(forRemoval);

				forRemoval.clear();
				for (DeclareAnnotation deca : decaToRepeat) {
					boolean didSomething = doDeclareAnnotations(deca, sourceType, false);
					if (didSomething) {
						if (factory.pushinCollector != null) {
							factory.pushinCollector.tagAsMunged(sourceType, deca.getAnnotationString());
						}
						anyNewAnnotations = true;
						forRemoval.add(deca);
					}
				}
				decaToRepeat.removeAll(forRemoval);
			}
		}
		if (mode == 0 || mode == 2) {
			for (Iterator<ConcreteTypeMunger> i = typeMungers.iterator(); i.hasNext();) {
				EclipseTypeMunger munger = (EclipseTypeMunger) i.next();
				if (munger.matches(onType)) {
					// if (needOldStyleWarning) {
					// factory.showMessage(IMessage.WARNING, "The class for " + onType
					// + " should be recompiled with ajc-1.1.1 for best results", onType.getSourceLocation(), null);
					// needOldStyleWarning = false;
					// }
					onType.addInterTypeMunger(munger, true);
					if (munger.getMunger() != null && munger.getMunger().getKind() == ResolvedTypeMunger.InnerClass) {
						// Must do these right now, because if we do an ITD member afterwards it may attempt to reference the
						// type being applied (the call above 'addInterTypeMunger' will fail for these ITDs if it needed
						// it to be in place)
						if (munger.munge(sourceType, onType)) {
							if (factory.pushinCollector != null) {
								factory.pushinCollector.tagAsMunged(sourceType, munger.getSourceMethod());
							}
						}
					}
				}
			}

			onType.checkInterTypeMungers();
			for (Iterator i = onType.getInterTypeMungers().iterator(); i.hasNext();) {
				EclipseTypeMunger munger = (EclipseTypeMunger) i.next();
				if (munger.getMunger() == null || munger.getMunger().getKind() != ResolvedTypeMunger.InnerClass) {
					if (munger.munge(sourceType, onType)) {
						if (factory.pushinCollector != null) {
							factory.pushinCollector.tagAsMunged(sourceType, munger.getSourceMethod());
						}
					}
				}
			}
		}

		// Call if you would like to do source weaving of declare
		// @method/@constructor
		// at source time... no need to do this as it can't impact anything, but
		// left here for
		// future generations to enjoy. Method source is commented out at the
		// end of this module
		// doDeclareAnnotationOnMethods();

		// Call if you would like to do source weaving of declare @field
		// at source time... no need to do this as it can't impact anything, but
		// left here for
		// future generations to enjoy. Method source is commented out at the
		// end of this module
		// doDeclareAnnotationOnFields();

		if (skipInners) {
			CompilationAndWeavingContext.leavingPhase(tok);
			return;
		}

		ReferenceBinding[] memberTypes = sourceType.memberTypes;
		for (int i = 0, length = memberTypes.length; i < length; i++) {
			if (memberTypes[i] instanceof SourceTypeBinding) {
				weaveInterTypeDeclarations((SourceTypeBinding) memberTypes[i], typeMungers, declareParents,
						declareAnnotationOnTypes, false, mode);
			}
		}
		CompilationAndWeavingContext.leavingPhase(tok);
	}

	/**
	 * Called when we discover we are weaving intertype declarations on some type that has an existing 'WeaverStateInfo' object -
	 * this is typically some previously woven type that has been passed on the inpath.
	 *
	 * sourceType and onType are the 'same type' - the former is the 'Eclipse' version and the latter is the 'Weaver' version.
	 */
	private void processTypeMungersFromExistingWeaverState(SourceTypeBinding sourceType, ResolvedType onType) {
		List<ConcreteTypeMunger> previouslyAppliedMungers = onType.getWeaverState().getTypeMungers(onType);

		for (Iterator<ConcreteTypeMunger> i = previouslyAppliedMungers.iterator(); i.hasNext();) {
			ConcreteTypeMunger m = i.next();
			EclipseTypeMunger munger = factory.makeEclipseTypeMunger(m);
			if (munger.munge(sourceType, onType)) {
				if (onType.isInterface() && munger.getMunger().needsAccessToTopmostImplementor()) {
					if (!onType.getWorld().getCrosscuttingMembersSet().containsAspect(munger.getAspectType())) {
						dangerousInterfaces
								.put(onType, "implementors of " + onType + " must be woven by " + munger.getAspectType());
					}
				}
			}

		}
	}

	private boolean doDeclareParents(DeclareParents declareParents, SourceTypeBinding sourceType) {
		ContextToken tok = CompilationAndWeavingContext.enteringPhase(CompilationAndWeavingContext.PROCESSING_DECLARE_PARENTS,
				sourceType.sourceName);
		ResolvedType resolvedSourceType = factory.fromEclipse(sourceType);
		List<ResolvedType> newParents = declareParents.findMatchingNewParents(resolvedSourceType, false);
		if (!newParents.isEmpty()) {
			for (Iterator<ResolvedType> i = newParents.iterator(); i.hasNext();) {
				ResolvedType parent = i.next();
				if (dangerousInterfaces.containsKey(parent)) {
					ResolvedType onType = factory.fromEclipse(sourceType);
					factory.showMessage(IMessage.ERROR, onType + ": " + dangerousInterfaces.get(parent),
							onType.getSourceLocation(), null);
				}
				if (Modifier.isFinal(parent.getModifiers())) {
					factory.showMessage(IMessage.ERROR, "cannot extend final class " + parent.getClassName(),
							declareParents.getSourceLocation(), null);
				} else {
					// do not actually do it if the type isn't exposed - this
					// will correctly reported as a problem elsewhere
					if (!resolvedSourceType.isExposedToWeaver()) {
						return false;
					}
					// AsmRelationshipProvider.getDefault().
					// addDeclareParentsRelationship
					// (declareParents.getSourceLocation(),
					// factory.fromEclipse(sourceType), newParents);
					addParent(sourceType, parent);
				}
			}
			CompilationAndWeavingContext.leavingPhase(tok);
			return true;
		}
		CompilationAndWeavingContext.leavingPhase(tok);
		return false;
	}

	private String stringifyTargets(long bits) {
		if ((bits & TagBits.AnnotationTargetMASK) == 0) {
			return "";
		}
		Set<String> s = new HashSet<String>();
		if ((bits & TagBits.AnnotationForAnnotationType) != 0) {
			s.add("ANNOTATION_TYPE");
		}
		if ((bits & TagBits.AnnotationForConstructor) != 0) {
			s.add("CONSTRUCTOR");
		}
		if ((bits & TagBits.AnnotationForField) != 0) {
			s.add("FIELD");
		}
		if ((bits & TagBits.AnnotationForLocalVariable) != 0) {
			s.add("LOCAL_VARIABLE");
		}
		if ((bits & TagBits.AnnotationForMethod) != 0) {
			s.add("METHOD");
		}
		if ((bits & TagBits.AnnotationForPackage) != 0) {
			s.add("PACKAGE");
		}
		if ((bits & TagBits.AnnotationForParameter) != 0) {
			s.add("PARAMETER");
		}
		if ((bits & TagBits.AnnotationForType) != 0) {
			s.add("TYPE");
		}
		StringBuffer sb = new StringBuffer();
		sb.append("{");
		for (Iterator<String> iter = s.iterator(); iter.hasNext();) {
			String element = iter.next();
			sb.append(element);
			if (iter.hasNext()) {
				sb.append(",");
			}
		}
		sb.append("}");
		return sb.toString();
	}

	private boolean doDeclareAnnotations(DeclareAnnotation decA, SourceTypeBinding sourceType, boolean reportProblems) {
		ResolvedType rtx = factory.fromEclipse(sourceType);
		if (!decA.matches(rtx)) {
			return false;
		}
		if (!rtx.isExposedToWeaver()) {
			return false;
		}

		ContextToken tok = CompilationAndWeavingContext.enteringPhase(CompilationAndWeavingContext.PROCESSING_DECLARE_ANNOTATIONS,
				sourceType.sourceName);

		// Get the annotation specified in the declare
		UnresolvedType aspectType = decA.getAspect();
		if (aspectType instanceof ReferenceType) {
			ReferenceType rt = (ReferenceType) aspectType;
			if (rt.isParameterizedType() || rt.isRawType()) {
				aspectType = rt.getGenericType();
			}
		}
		TypeBinding tb = factory.makeTypeBinding(aspectType);

		// Hideousness follows:

		// There are multiple situations to consider here and they relate to the
		// combinations of
		// where the annotation is coming from and where the annotation is going
		// to be put:
		//
		// 1. Straight full build, all from source - the annotation is from a
		// dec@type and
		// is being put on some type. Both types are real SourceTypeBindings.
		// WORKS
		// 2. Incremental build, changing the affected type - the annotation is
		// from a
		// dec@type in a BinaryTypeBinding (so has to be accessed via bcel) and
		// the
		// affected type is a real SourceTypeBinding. Mostly works (pr128665)
		// 3. ?

		SourceTypeBinding stb = (SourceTypeBinding) tb;
		Annotation[] toAdd = null;
		long abits = 0;

		AbstractMethodDeclaration methodDecl = null;
		// Might have to retrieve the annotation through BCEL and construct an
		// eclipse one for it.
		if (stb instanceof BinaryTypeBinding) {
			toAdd = retrieveAnnotationFromBinaryTypeBinding(decA, stb);
			if (toAdd != null && toAdd.length > 0 && toAdd[0].resolvedType != null) {
				abits = toAdd[0].resolvedType.getAnnotationTagBits();
			}
		} else if (stb != null) {
			// much nicer, its a real SourceTypeBinding so we can stay in
			// eclipse land
			// if (decA.getAnnotationMethod() != null) {
			char[] declareSelector = decA.getAnnotationMethod().toCharArray();

			ReferenceBinding rb = stb;
			String declaringAspectName = decA.getDeclaringType().getRawName();
			while (rb != null && !new String(CharOperation.concatWith(rb.compoundName, '.')).equals(declaringAspectName)) {
				rb = rb.superclass();
			}
			MethodBinding[] mbs = rb.getMethods(declareSelector);

			ReferenceBinding declaringBinding = mbs[0].declaringClass;
			if (declaringBinding instanceof ParameterizedTypeBinding) {
				// Unwrap - this means we don't allow the type of the annotation to be parameterized, may need to revisit that
				declaringBinding = ((ParameterizedTypeBinding) declaringBinding).type;
			}
			if (declaringBinding instanceof BinaryTypeBinding) {
				toAdd = retrieveAnnotationFromBinaryTypeBinding(decA, declaringBinding);
				if (toAdd != null && toAdd.length > 0 && toAdd[0].resolvedType != null) {
					abits = toAdd[0].resolvedType.getAnnotationTagBits();
				}
			} else {
				abits = mbs[0].getAnnotationTagBits(); // ensure resolved
				TypeDeclaration typeDecl = ((SourceTypeBinding) declaringBinding).scope.referenceContext;
				methodDecl = typeDecl.declarationOf(mbs[0]);
				toAdd = methodDecl.annotations; // this is what to add
				toAdd[0] = createAnnotationCopy(toAdd[0]);
				if (toAdd[0].resolvedType != null) {
					abits = toAdd[0].resolvedType.getAnnotationTagBits();
					// }
				}
			}
		}

		// This happens if there is another error in the code - that should be reported separately
		if (toAdd == null || toAdd[0] == null || toAdd[0].type == null) {
			CompilationAndWeavingContext.leavingPhase(tok);
			return false;
		}
		if (sourceType instanceof BinaryTypeBinding) {
			// In this case we can't access the source type binding to add a new
			// annotation, so let's put something
			// on the weaver type temporarily
			ResolvedType theTargetType = factory.fromEclipse(sourceType);
			TypeBinding theAnnotationType = toAdd[0].resolvedType;
			// The annotation type may be null if it could not be resolved (eg. the relevant import has not been added yet)
			// In this case an error will be put out about the annotation but not if we crash here
			if (theAnnotationType == null) {
				return false;
			}
			String sig = new String(theAnnotationType.signature());
			UnresolvedType bcelAnnotationType = UnresolvedType.forSignature(sig);
			String name = bcelAnnotationType.getName();
			if (theTargetType.hasAnnotation(bcelAnnotationType)) {
				CompilationAndWeavingContext.leavingPhase(tok);
				return false;
			}

			// FIXME asc tidy up this code that duplicates whats below!
			// Simple checks on the bits
			boolean giveupnow = false;
			if (((abits & TagBits.AnnotationTargetMASK) != 0)) {
				if (isAnnotationTargettingSomethingOtherThanAnnotationOrNormal(abits)) {
					// error will have been already reported
					giveupnow = true;
				} else if ((sourceType.isAnnotationType() && (abits & TagBits.AnnotationForAnnotationType) == 0)
						|| (!sourceType.isAnnotationType() && (abits & TagBits.AnnotationForType) == 0)) {

					if (reportProblems) {
						if (decA.isExactPattern()) {
							factory.showMessage(IMessage.ERROR, WeaverMessages.format(
									WeaverMessages.INCORRECT_TARGET_FOR_DECLARE_ANNOTATION, rtx.getName(), toAdd[0].type,
									stringifyTargets(abits)), decA.getSourceLocation(), null);
						}
						// dont put out the lint - the weaving process will do
						// that
						// else {
						// if (factory.getWorld().getLint().
						// invalidTargetForAnnotation.isEnabled()) {
						// factory.getWorld().getLint().invalidTargetForAnnotation
						// .signal(new
						// String[]{rtx.getName(),toAdd[0].type.toString(),
						// stringifyTargets
						// (abits)},decA.getSourceLocation(),null);
						// }
						// }
					}
					giveupnow = true;
				}
			}
			if (giveupnow) {
				CompilationAndWeavingContext.leavingPhase(tok);
				return false;
			}

			theTargetType.addAnnotation(new BcelAnnotation(new FakeAnnotation(name, sig,
					(abits & TagBits.AnnotationRuntimeRetention) != 0), factory.getWorld()));
			CompilationAndWeavingContext.leavingPhase(tok);
			return true;
		}

		Annotation currentAnnotations[] = sourceType.scope.referenceContext.annotations;
		if (currentAnnotations != null) {
			for (int i = 0; i < currentAnnotations.length; i++) {
				Annotation annotation = currentAnnotations[i];
				String a = CharOperation.toString(annotation.type.getTypeName());
				String b = CharOperation.toString(toAdd[0].type.getTypeName());
				// FIXME asc we have a lint for attempting to add an annotation
				// twice to a method,
				// we could put it out here *if* we can resolve the problem of
				// errors coming out
				// multiple times if we have cause to loop through here
				if (a.equals(b)) {
					CompilationAndWeavingContext.leavingPhase(tok);
					return false;
				}
			}
		}

		if (((abits & TagBits.AnnotationTargetMASK) != 0)) {
			if ((abits & (TagBits.AnnotationForAnnotationType | TagBits.AnnotationForType)) == 0) {
				// this means it specifies something other than annotation or
				// normal type - error will have been already reported,
				// just resolution process above
				CompilationAndWeavingContext.leavingPhase(tok);
				return false;
			}
			if ((sourceType.isAnnotationType() && (abits & TagBits.AnnotationForAnnotationType) == 0)
					|| (!sourceType.isAnnotationType() && (abits & TagBits.AnnotationForType) == 0)) {

				if (reportProblems) {
					if (decA.isExactPattern()) {
						factory.showMessage(IMessage.ERROR, WeaverMessages.format(
								WeaverMessages.INCORRECT_TARGET_FOR_DECLARE_ANNOTATION, rtx.getName(), toAdd[0].type,
								stringifyTargets(abits)), decA.getSourceLocation(), null);
					}
					// dont put out the lint - the weaving process will do that
					// else {
					// if
					// (factory.getWorld().getLint().invalidTargetForAnnotation
					// .isEnabled()) {
					// factory.getWorld().getLint().invalidTargetForAnnotation.
					// signal(new
					// String[]{rtx.getName(),toAdd[0].type.toString(),
					// stringifyTargets(abits)},decA.getSourceLocation(),null);
					// }
					// }
				}
				CompilationAndWeavingContext.leavingPhase(tok);
				return false;
			}
		}

		// Build a new array of annotations

		// remember the current set (rememberAnnotations only does something the
		// first time it is called for a type)
		sourceType.scope.referenceContext.rememberAnnotations();

		// AsmRelationshipProvider.getDefault().addDeclareAnnotationRelationship(
		// decA.getSourceLocation(), rtx.getSourceLocation());
		final Annotation[] abefore = sourceType.scope.referenceContext.annotations;
		final Annotation[] newset = new Annotation[toAdd.length + (abefore == null ? 0 : abefore.length)];
		System.arraycopy(toAdd, 0, newset, 0, toAdd.length);
		if (abefore != null) {
			System.arraycopy(abefore, 0, newset, toAdd.length, abefore.length);
		}
		sourceType.scope.referenceContext.annotations = newset;
		if ((sourceType.tagBits & TagBits.AnnotationResolved)!=0) {
			sourceType.tagBits = sourceType.tagBits - TagBits.AnnotationResolved;
		}
		CompilationAndWeavingContext.leavingPhase(tok);
		if (factory.pushinCollector != null) {
			factory.pushinCollector.tagAsMunged(sourceType, new CommonPrinter((methodDecl == null ? null : methodDecl.scope))
					.printAnnotation(toAdd[0]).toString());
		}
		return true;
	}

	private Annotation[] retrieveAnnotationFromBinaryTypeBinding(DeclareAnnotation decA, ReferenceBinding declaringBinding) {
		ReferenceType rt = (ReferenceType) factory.fromEclipse(declaringBinding);
		ResolvedMember[] methods = rt.getDeclaredMethods();
		ResolvedMember decaMethod = null;
		String nameToLookFor = decA.getAnnotationMethod();
		for (int i = 0; i < methods.length; i++) {
			if (methods[i].getName().equals(nameToLookFor)) {
				decaMethod = methods[i];
				break;
			}
		}
		if (decaMethod != null) { // could assert this ...
			AnnotationAJ[] axs = decaMethod.getAnnotations();
			if (axs != null) { // another error has occurred, dont crash here because of it
				Annotation[] toAdd = new Annotation[1];
				toAdd[0] = createAnnotationFromBcelAnnotation(axs[0], decaMethod.getSourceLocation().getOffset(), factory);
				// BUG BUG BUG - We dont test these abits are correct, in fact
				// we'll be very lucky if they are.
				// What does that mean? It means on an incremental compile you
				// might get away with an
				// annotation that isn't allowed on a type being put on a type.
				// if (toAdd[0].resolvedType != null) {
				// abits = toAdd[0].resolvedType.getAnnotationTagBits();
				// }
				return toAdd;
			}
		}
		return null;
	}

	/**
	 * Transform an annotation from its AJ form to an eclipse form. We *DONT* care about the values of the annotation. that is
	 * because it is only being stuck on a type during type completion to allow for other constructs (decps, decas) that might be
	 * looking for it - when the class actually gets to disk it wont have this new annotation on it and during weave time we will do
	 * the right thing copying across values too.
	 */
	private static Annotation createAnnotationFromBcelAnnotation(AnnotationAJ annX, int pos, EclipseFactory factory) {
		String name = annX.getTypeName();
		TypeBinding tb = factory.makeTypeBinding(annX.getType());
		// String theName = annX.getSignature().getBaseName();
		char[][] typeName = CharOperation.splitOn('.', name.replace('$', '.').toCharArray()); // pr149293 - not bulletproof...
		long[] positions = new long[typeName.length];
		for (int i = 0; i < positions.length; i++) {
			positions[i] = pos;
		}
		TypeReference annType = new QualifiedTypeReference(typeName, positions);
		NormalAnnotation ann = new NormalAnnotation(annType, pos);
		ann.resolvedType = tb; // yuck - is this OK in all cases?
		// We don't need membervalues...
		// Expression pcExpr = new
		// StringLiteral(pointcutExpression.toCharArray(),pos,pos);
		// MemberValuePair[] mvps = new MemberValuePair[2];
		// mvps[0] = new MemberValuePair("value".toCharArray(),pos,pos,pcExpr);
		// Expression argNamesExpr = new
		// StringLiteral(argNames.toCharArray(),pos,pos);
		// mvps[1] = new
		// MemberValuePair("argNames".toCharArray(),pos,pos,argNamesExpr);
		// ann.memberValuePairs = mvps;
		return ann;
	}

	/**
	 * Create a copy of an annotation, not deep but deep enough so we don't copy across fields that will get us into trouble like
	 * 'recipient'
	 */
	private static Annotation createAnnotationCopy(Annotation ann) {
		NormalAnnotation ann2 = new NormalAnnotation(ann.type, ann.sourceStart);
		ann2.memberValuePairs = ann.memberValuePairs();
		ann2.resolvedType = ann.resolvedType;
		ann2.bits = ann.bits;
		return ann2;
		// String name = annX.getTypeName();
		// TypeBinding tb = factory.makeTypeBinding(annX.getSignature());
		// String theName = annX.getSignature().getBaseName();
		// char[][] typeName =
		// CharOperation.splitOn('.',name.replace('$','.').toCharArray());
		// //pr149293 - not bulletproof...
		// long[] positions = new long[typeName.length];
		// for (int i = 0; i < positions.length; i++) positions[i]=pos;
		// TypeReference annType = new
		// QualifiedTypeReference(typeName,positions);
		// NormalAnnotation ann = new NormalAnnotation(annType,pos);
		// ann.resolvedType=tb; // yuck - is this OK in all cases?
		// // We don't need membervalues...
		// // Expression pcExpr = new
		// StringLiteral(pointcutExpression.toCharArray(),pos,pos);
		// // MemberValuePair[] mvps = new MemberValuePair[2];
		// // mvps[0] = new
		// MemberValuePair("value".toCharArray(),pos,pos,pcExpr);
		// // Expression argNamesExpr = new
		// StringLiteral(argNames.toCharArray(),pos,pos);
		// // mvps[1] = new
		// MemberValuePair("argNames".toCharArray(),pos,pos,argNamesExpr);
		// // ann.memberValuePairs = mvps;
		// return ann;
	}

	private boolean isAnnotationTargettingSomethingOtherThanAnnotationOrNormal(long abits) {
		return (abits & (TagBits.AnnotationForAnnotationType | TagBits.AnnotationForType)) == 0;
	}

//	private void reportDeclareParentsMessage(WeaveMessage.WeaveMessageKind wmk, SourceTypeBinding sourceType, ResolvedType parent) {
//		if (!factory.getWorld().getMessageHandler().isIgnoring(IMessage.WEAVEINFO)) {
//			String filename = new String(sourceType.getFileName());
//
//			int takefrom = filename.lastIndexOf('/');
//			if (takefrom == -1) {
//				takefrom = filename.lastIndexOf('\\');
//			}
//			filename = filename.substring(takefrom + 1);
//
//			factory.getWorld()
//					.getMessageHandler()
//					.handleMessage(
//							WeaveMessage.constructWeavingMessage(wmk,
//									new String[] { CharOperation.toString(sourceType.compoundName), filename,
//											parent.getClassName(),
//											getShortname(parent.getSourceLocation().getSourceFile().getPath()) }));
//		}
//	}

//	private String getShortname(String path) {
//		int takefrom = path.lastIndexOf('/');
//		if (takefrom == -1) {
//			takefrom = path.lastIndexOf('\\');
//		}
//		return path.substring(takefrom + 1);
//	}

	private void addParent(SourceTypeBinding sourceType, ResolvedType parent) {
		ReferenceBinding parentBinding = (ReferenceBinding) factory.makeTypeBinding(parent);
		if (parentBinding == null) {
			return; // The parent is missing, it will be reported elsewhere.
		}
		// Due to e37 switching to MethodVerifier15 for everything, it is important added types are correctly
		// raw or not.  For example, if Comparable is used in generic form compareTo(T) will be used to check
		// methods against in the verifier rather than compareTo(Object)
		if (!factory.getWorld().isInJava5Mode()) {
			parentBinding = (ReferenceBinding)convertToRawType(parentBinding, false /*do not force conversion of enclosing types*/);
		} else if (sourceType.isGenericType()) {
			RawTypeBinding rawTargetType = (RawTypeBinding)convertToRawType(sourceType, false);
			if (rawTargetType != null) {
				// assert: don't need to 'rememberTypeHierarchy' because the class file is constructed based on the generic type
				if (parentBinding.isClass()) {
					rawTargetType.superclass = parentBinding;
				} else {
					ReferenceBinding[] oldI = rawTargetType.superInterfaces;
					ReferenceBinding[] newI;
					if (oldI == null) {
						newI = new ReferenceBinding[1];
						newI[0] = parentBinding;
					} else {
						int n = oldI.length;
						newI = new ReferenceBinding[n + 1];
						System.arraycopy(oldI, 0, newI, 0, n);
						newI[n] = parentBinding;
					}
					rawTargetType.superInterfaces = newI;
				}
			}
			// TODO what about parameterized types?
		}
		sourceType.rememberTypeHierarchy();
		if (parentBinding.isClass()) {
			sourceType.superclass = parentBinding;

			// this used to be true, but I think I've fixed it now, decp is done
			// at weave time!
			// TAG: WeavingMessage DECLARE PARENTS: EXTENDS
			// Compiler restriction: Can't do EXTENDS at weave time
			// So, only see this message if doing a source compilation
			// reportDeclareParentsMessage(WeaveMessage.
			// WEAVEMESSAGE_DECLAREPARENTSEXTENDS,sourceType,parent);

		} else {
			ReferenceBinding[] oldI = sourceType.superInterfaces;
			ReferenceBinding[] newI;
			if (oldI == null) {
				newI = new ReferenceBinding[1];
				newI[0] = parentBinding;
			} else {
				int n = oldI.length;
				newI = new ReferenceBinding[n + 1];
				System.arraycopy(oldI, 0, newI, 0, n);
				newI[n] = parentBinding;
			}
			sourceType.superInterfaces = newI;
			// warnOnAddedInterface(factory.fromEclipse(sourceType),parent); //
			// now reported at weave time...

			// this used to be true, but I think I've fixed it now, decp is done
			// at weave time!
			// TAG: WeavingMessage DECLARE PARENTS: IMPLEMENTS
			// This message will come out of BcelTypeMunger.munge if doing a
			// binary weave
			// reportDeclareParentsMessage(WeaveMessage.
			// WEAVEMESSAGE_DECLAREPARENTSIMPLEMENTS,sourceType,parent);

		}

		// also add it to the bcel delegate if there is one
		if (sourceType instanceof BinaryTypeBinding) {
			ResolvedType onType = factory.fromEclipse(sourceType);
			ReferenceType rt = (ReferenceType) onType;
			ReferenceTypeDelegate rtd = rt.getDelegate();
			if (rtd instanceof BcelObjectType) {
				if (rt.isRawType()) {
					rt = (ReferenceType)rt.getGenericType();
				}
				rt.addParent(parent);
				// ((BcelObjectType) rtd).addParent(parent);
			}
		}

	}

	public void warnOnAddedInterface(ResolvedType type, ResolvedType parent) {
		World world = factory.getWorld();
		ResolvedType serializable = world.getCoreType(UnresolvedType.SERIALIZABLE);
		if (serializable.isAssignableFrom(type) && !serializable.isAssignableFrom(parent)
				&& !LazyClassGen.hasSerialVersionUIDField(type)) {
			world.getLint().needsSerialVersionUIDField.signal(new String[] { type.getName().toString(),
					"added interface " + parent.getName().toString() }, null, null);
		}
	}

	private final List pendingTypesToFinish = new ArrayList();
	boolean inBinaryTypeCreationAndWeaving = false;
	boolean processingTheQueue = false;

	public BinaryTypeBinding createBinaryTypeFrom(IBinaryType binaryType, PackageBinding packageBinding,
			boolean needFieldsAndMethods, AccessRestriction accessRestriction) {

		if (inBinaryTypeCreationAndWeaving) {
			BinaryTypeBinding ret = super.createBinaryTypeFrom(binaryType, packageBinding, needFieldsAndMethods, accessRestriction);
			pendingTypesToFinish.add(ret);
			return ret;
		}

		inBinaryTypeCreationAndWeaving = true;
		try {
			BinaryTypeBinding ret = super.createBinaryTypeFrom(binaryType, packageBinding, needFieldsAndMethods, accessRestriction);
			factory.getWorld().validateType(factory.fromBinding(ret));
			// if you need the bytes to pass to validate, here they
			// are:((ClassFileReader)binaryType).getReferenceBytes()
			weaveInterTypeDeclarations(ret);
			return ret;
		} finally {
			inBinaryTypeCreationAndWeaving = false;

			// Start processing the list...
			if (pendingTypesToFinish.size() > 0) {
				processingTheQueue = true;
				while (!pendingTypesToFinish.isEmpty()) {
					BinaryTypeBinding nextVictim = (BinaryTypeBinding) pendingTypesToFinish.remove(0);
					// During this call we may recurse into this method and add
					// more entries to the pendingTypesToFinish list.
					weaveInterTypeDeclarations(nextVictim);
				}
				processingTheQueue = false;
			}
		}
	}

	/**
	 * Callback driven when the compiler detects an anonymous type during block resolution. We need to add it to the weaver so that
	 * we don't trip up later.
	 *
	 * @param aBinding
	 */
	public void anonymousTypeBindingCreated(LocalTypeBinding aBinding) {
		factory.addSourceTypeBinding(aBinding, null);
	}

  @Override
  public void buildTypeBindings(CompilationUnitDeclaration unit, AccessRestriction accessRestriction) {
    if (this.isProcessingAnnotations && hasAspectDeclarations(unit)) {
      throw new SourceTypeCollisionException();
    }
    super.buildTypeBindings(unit, accessRestriction);
  }

  private static boolean hasAspectDeclarations(CompilationUnitDeclaration unit) {
    for (int j = 0; j < unit.types.length; j++) {
      if (unit.types[j] instanceof AspectDeclaration) {
        return true;
      }
    }
    return false;
  }

  @Override
  public void reset() {
    this.factory.cleanup();
    super.reset();
  }
}

// commented out, supplied as info on how to manipulate annotations in an
// eclipse world
//
// public void doDeclareAnnotationOnMethods() {
// Do the declare annotation on fields/methods/ctors
// Collection daoms = factory.getDeclareAnnotationOnMethods();
// if (daoms!=null && daoms.size()>0 && !(sourceType instanceof
// BinaryTypeBinding)) {
// System.err.println("Going through the methods on "+sourceType.debugName()+
// " looking for DECA matches");
// // We better take a look through them...
// for (Iterator iter = daoms.iterator(); iter.hasNext();) {
// DeclareAnnotation element = (DeclareAnnotation) iter.next();
// System.err.println("Looking for anything that might match "+element+" on "+
// sourceType.debugName()+"  "+getType(sourceType.
// compoundName).debugName()+"  "+(sourceType instanceof BinaryTypeBinding));
//
// ReferenceBinding rbb = getType(sourceType.compoundName);
// // fix me if we ever uncomment this code... should iterate the other way
// round, over the methods then over the decas
// sourceType.methods();
// MethodBinding sourceMbs[] = sourceType.methods;
// for (int i = 0; i < sourceMbs.length; i++) {
// MethodBinding sourceMb = sourceMbs[i];
// MethodBinding mbbbb =
// ((SourceTypeBinding)rbb).getExactMethod(sourceMb.selector
// ,sourceMb.parameters);
// boolean isCtor = sourceMb.selector[0]=='<';
//
// if ((element.isDeclareAtConstuctor() ^ !isCtor)) {
// System.err.println("Checking "+sourceMb+" ... declaringclass="+sourceMb.
// declaringClass.debugName()+" rbb="+rbb.debugName()+"  "+
// sourceMb.declaringClass.equals(rbb));
//
// ResolvedMember rm = null;
// rm = EclipseFactory.makeResolvedMember(mbbbb);
// if (element.matches(rm,factory.getWorld())) {
// System.err.println("MATCH");
//
// // Determine the set of annotations that are currently on the method
// ReferenceBinding rb = getType(sourceType.compoundName);
// // TypeBinding tb = factory.makeTypeBinding(decA.getAspect());
// MethodBinding mb =
// ((SourceTypeBinding)rb).getExactMethod(sourceMb.selector,sourceMb
// .parameters);
// //long abits = mbs[0].getAnnotationTagBits(); // ensure resolved
// TypeDeclaration typeDecl =
// ((SourceTypeBinding)sourceMb.declaringClass).scope.referenceContext;
// AbstractMethodDeclaration methodDecl = typeDecl.declarationOf(sourceMb);
// Annotation[] currentlyHas = methodDecl.annotations; // this is what to add
// //abits = toAdd[0].resolvedType.getAnnotationTagBits();
//
// // Determine the annotations to add to that method
// TypeBinding tb = factory.makeTypeBinding(element.getAspect());
// MethodBinding[] aspectMbs =
// ((SourceTypeBinding)tb).getMethods(element.getAnnotationMethod
// ().toCharArray());
// long abits = aspectMbs[0].getAnnotationTagBits(); // ensure resolved
// TypeDeclaration typeDecl2 =
// ((SourceTypeBinding)aspectMbs[0].declaringClass).scope.referenceContext;
// AbstractMethodDeclaration methodDecl2 =
// typeDecl2.declarationOf(aspectMbs[0]);
// Annotation[] toAdd = methodDecl2.annotations; // this is what to add
// // abits = toAdd[0].resolvedType.getAnnotationTagBits();
// System.err.println("Has: "+currentlyHas+"    toAdd: "+toAdd);
//
// // fix me? should check if it already has the annotation
// //Annotation abefore[] = sourceType.scope.referenceContext.annotations;
// Annotation[] newset = new
// Annotation[(currentlyHas==null?0:currentlyHas.length)+1];
// System.arraycopy(toAdd,0,newset,0,toAdd.length);
// if (currentlyHas!=null) {
// System.arraycopy(currentlyHas,0,newset,1,currentlyHas.length);
// }
// methodDecl.annotations = newset;
// System.err.println("New set on "+CharOperation.charToString(sourceMb.selector)
// +" is "+newset);
// } else
// System.err.println("NO MATCH");
// }
// }
// }
// }
// }

// commented out, supplied as info on how to manipulate annotations in an
// eclipse world
//
// public void doDeclareAnnotationOnFields() {
// Collection daofs = factory.getDeclareAnnotationOnFields();
// if (daofs!=null && daofs.size()>0 && !(sourceType instanceof
// BinaryTypeBinding)) {
// System.err.println("Going through the fields on "+sourceType.debugName()+
// " looking for DECA matches");
// // We better take a look through them...
// for (Iterator iter = daofs.iterator(); iter.hasNext();) {
// DeclareAnnotation element = (DeclareAnnotation) iter.next();
// System.err.println("Processing deca "+element+" on "+sourceType.debugName()+
// "  "+getType(sourceType.compoundName).debugName()+"  "
// +(sourceType instanceof BinaryTypeBinding));
//
// ReferenceBinding rbb = getType(sourceType.compoundName);
// // fix me? should iterate the other way round, over the methods then over the
// decas
// sourceType.fields(); // resolve the bloody things
// FieldBinding sourceFbs[] = sourceType.fields;
// for (int i = 0; i < sourceFbs.length; i++) {
// FieldBinding sourceFb = sourceFbs[i];
// //FieldBinding fbbbb =
// ((SourceTypeBinding)rbb).getgetExactMethod(sourceMb.selector
// ,sourceMb.parameters);
//
// System.err.println("Checking "+sourceFb+" ... declaringclass="+sourceFb.
// declaringClass.debugName()+" rbb="+rbb.debugName());
//
// ResolvedMember rm = null;
// rm = EclipseFactory.makeResolvedMember(sourceFb);
// if (element.matches(rm,factory.getWorld())) {
// System.err.println("MATCH");
//
// // Determine the set of annotations that are currently on the field
// ReferenceBinding rb = getType(sourceType.compoundName);
// // TypeBinding tb = factory.makeTypeBinding(decA.getAspect());
// FieldBinding fb = ((SourceTypeBinding)rb).getField(sourceFb.name,true);
// //long abits = mbs[0].getAnnotationTagBits(); // ensure resolved
// TypeDeclaration typeDecl =
// ((SourceTypeBinding)sourceFb.declaringClass).scope.referenceContext;
// FieldDeclaration fd = typeDecl.declarationOf(sourceFb);
// //AbstractMethodDeclaration methodDecl = typeDecl.declarationOf(sourceMb);
// Annotation[] currentlyHas = fd.annotations; // this is what to add
// //abits = toAdd[0].resolvedType.getAnnotationTagBits();
//
// // Determine the annotations to add to that method
// TypeBinding tb = factory.makeTypeBinding(element.getAspect());
// MethodBinding[] aspectMbs =
// ((SourceTypeBinding)tb).getMethods(element.getAnnotationMethod
// ().toCharArray());
// long abits = aspectMbs[0].getAnnotationTagBits(); // ensure resolved
// TypeDeclaration typeDecl2 =
// ((SourceTypeBinding)aspectMbs[0].declaringClass).scope.referenceContext;
// AbstractMethodDeclaration methodDecl2 =
// typeDecl2.declarationOf(aspectMbs[0]);
// Annotation[] toAdd = methodDecl2.annotations; // this is what to add
// // abits = toAdd[0].resolvedType.getAnnotationTagBits();
// System.err.println("Has: "+currentlyHas+"    toAdd: "+toAdd);
//
// // fix me? check if it already has the annotation
//
//
// //Annotation abefore[] = sourceType.scope.referenceContext.annotations;
// Annotation[] newset = new
// Annotation[(currentlyHas==null?0:currentlyHas.length)+1];
// System.arraycopy(toAdd,0,newset,0,toAdd.length);
// if (currentlyHas!=null) {
// System.arraycopy(currentlyHas,0,newset,1,currentlyHas.length);
// }
// fd.annotations = newset;
// System.err.println("New set on "+CharOperation.charToString(sourceFb.name)+
// " is "+newset);
// } else
// System.err.println("NO MATCH");
// }
//
// }
// }
