/* *******************************************************************
 * 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 
 *     Mik Kersten	2004-07-26 extended to allow overloading of 
 * 					hierarchy builder
 * ******************************************************************/

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

import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.aspectj.ajdt.internal.compiler.ast.AspectDeclaration;
import org.aspectj.ajdt.internal.compiler.ast.AstUtil;
import org.aspectj.ajdt.internal.core.builder.AjBuildManager;
import org.aspectj.bridge.IMessage.Kind;
import org.aspectj.bridge.ISourceLocation;
import org.aspectj.org.eclipse.jdt.core.Flags;
import org.aspectj.org.eclipse.jdt.core.compiler.CharOperation;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.EmptyStatement;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.Wildcard;
import org.aspectj.org.eclipse.jdt.internal.compiler.impl.Constant;
import org.aspectj.org.eclipse.jdt.internal.compiler.impl.ReferenceContext;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ArrayBinding;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.LocalTypeBinding;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ProblemReasons;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ProblemReferenceBinding;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.RawTypeBinding;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.Scope;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.SyntheticFieldBinding;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.WildcardBinding;
import org.aspectj.weaver.BCException;
import org.aspectj.weaver.BoundedReferenceType;
import org.aspectj.weaver.ConcreteTypeMunger;
import org.aspectj.weaver.IHasPosition;
import org.aspectj.weaver.Member;
import org.aspectj.weaver.MemberKind;
import org.aspectj.weaver.NameMangler;
import org.aspectj.weaver.NewFieldTypeMunger;
import org.aspectj.weaver.NewMethodTypeMunger;
import org.aspectj.weaver.ReferenceType;
import org.aspectj.weaver.ResolvedMember;
import org.aspectj.weaver.ResolvedMemberImpl;
import org.aspectj.weaver.ResolvedType;
import org.aspectj.weaver.ResolvedTypeMunger;
import org.aspectj.weaver.Shadow;
import org.aspectj.weaver.TypeFactory;
import org.aspectj.weaver.TypeVariable;
import org.aspectj.weaver.TypeVariableDeclaringElement;
import org.aspectj.weaver.TypeVariableReference;
import org.aspectj.weaver.UnresolvedType;
import org.aspectj.weaver.UnresolvedType.TypeKind;
import org.aspectj.weaver.UnresolvedTypeVariableReferenceType;
import org.aspectj.weaver.WildcardedUnresolvedType;
import org.aspectj.weaver.World;
import org.aspectj.weaver.patterns.DeclareAnnotation;
import org.aspectj.weaver.patterns.DeclareParents;

/**
 * @author Jim Hugunin
 */
public class EclipseFactory {
	public static boolean DEBUG = false;
	public static int debug_mungerCount = -1;

	private final AjBuildManager buildManager;
	private final LookupEnvironment lookupEnvironment;
	private final boolean xSerializableAspects;
	private final World world;
	public PushinCollector pushinCollector;
	public List<ConcreteTypeMunger> finishedTypeMungers = null;

	// We can get clashes if we don't treat raw types differently - we end up looking
	// up a raw and getting the generic type (pr115788)
	private final Map<UnresolvedType, TypeBinding> typexToBinding = new HashMap<UnresolvedType, TypeBinding>();
	private final Map<UnresolvedType, TypeBinding> rawTypeXToBinding = new HashMap<UnresolvedType, TypeBinding>();

	// XXX currently unused
	// private Map/*TypeBinding, ResolvedType*/ bindingToResolvedTypeX = new HashMap();

	public static EclipseFactory fromLookupEnvironment(LookupEnvironment env) {
		AjLookupEnvironment aenv = (AjLookupEnvironment) env;
		return aenv.factory;
	}

	public LookupEnvironment getLookupEnvironment() {
		return this.lookupEnvironment;
	}

	public static EclipseFactory fromScopeLookupEnvironment(Scope scope) {
		return fromLookupEnvironment(AstUtil.getCompilationUnitScope(scope).environment);
	}

	public EclipseFactory(LookupEnvironment lookupEnvironment, AjBuildManager buildManager) {
		this.lookupEnvironment = lookupEnvironment;
		this.buildManager = buildManager;
		this.world = buildManager.getWorld();
		this.pushinCollector = PushinCollector.createInstance(this.world);
		this.xSerializableAspects = buildManager.buildConfig.isXserializableAspects();
	}

	public EclipseFactory(LookupEnvironment lookupEnvironment, World world, boolean xSer) {
		this.lookupEnvironment = lookupEnvironment;
		this.world = world;
		this.xSerializableAspects = xSer;
		this.pushinCollector = PushinCollector.createInstance(this.world);
		this.buildManager = null;
	}

	public World getWorld() {
		return world;
	}

	public void showMessage(Kind kind, String message, ISourceLocation loc1, ISourceLocation loc2) {
		getWorld().showMessage(kind, message, loc1, loc2);
	}

	public ResolvedType fromEclipse(ReferenceBinding binding) {
		if (binding == null) {
			return ResolvedType.MISSING;
		}
		// ??? this seems terribly inefficient 
		// System.err.println("resolving: " + binding.getClass() + ", name = " + getName(binding));
		ResolvedType ret = getWorld().resolve(fromBinding(binding));
		// System.err.println("      got: " + ret);
		return ret;
	}

	public ResolvedType fromTypeBindingToRTX(TypeBinding tb) {
		if (tb == null) {
			return ResolvedType.MISSING;
		}
		ResolvedType ret = getWorld().resolve(fromBinding(tb));
		return ret;
	}

	public ResolvedType[] fromEclipse(ReferenceBinding[] bindings) {
		if (bindings == null) {
			return ResolvedType.NONE;
		}
		int len = bindings.length;
		ResolvedType[] ret = new ResolvedType[len];
		for (int i = 0; i < len; i++) {
			ret[i] = fromEclipse(bindings[i]);
		}
		return ret;
	}

	public static String getName(TypeBinding binding) {
		if (binding instanceof TypeVariableBinding) {
			// The first bound may be null - so default to object?
			TypeVariableBinding tvb = (TypeVariableBinding) binding;
			if (tvb.firstBound != null) {
				return getName(tvb.firstBound);
			} else {
				return getName(tvb.superclass);
			}
		}

		if (binding instanceof ReferenceBinding) {
			return new String(CharOperation.concatWith(((ReferenceBinding) binding).compoundName, '.'));
		}

		String packageName = new String(binding.qualifiedPackageName());
		String className = new String(binding.qualifiedSourceName()).replace('.', '$');
		if (packageName.length() > 0) {
			className = packageName + "." + className;
		}
		// XXX doesn't handle arrays correctly (or primitives?)
		return new String(className);
	}

	/**
	 * Some generics notes:
	 * 
	 * Andy 6-May-05 We were having trouble with parameterized types in a couple of places - due to TypeVariableBindings. When we
	 * see a TypeVariableBinding now we default to either the firstBound if it is specified or java.lang.Object. Not sure when/if
	 * this gets us unstuck? It does mean we forget that it is a type variable when going back the other way from the UnresolvedType
	 * and that would seem a bad thing - but I've yet to see the reason we need to remember the type variable. Adrian 10-July-05
	 * When we forget it's a type variable we come unstuck when getting the declared members of a parameterized type - since we
	 * don't know it's a type variable we can't replace it with the type parameter.
	 */
	// ??? going back and forth between strings and bindings is a waste of cycles
	public UnresolvedType fromBinding(TypeBinding binding) {
		if (binding instanceof HelperInterfaceBinding) {
			return ((HelperInterfaceBinding) binding).getTypeX();
		}
		if (binding == null || binding.qualifiedSourceName() == null) {
			return ResolvedType.MISSING;
		}
		// first piece of generics support!
		if (binding instanceof TypeVariableBinding) {
			TypeVariableBinding tb = (TypeVariableBinding) binding;
			UnresolvedTypeVariableReferenceType utvrt = (UnresolvedTypeVariableReferenceType) fromTypeVariableBinding(tb);
			return utvrt;
		}

		// handle arrays since the component type may need special treatment too...
		if (binding instanceof ArrayBinding) {
			ArrayBinding aBinding = (ArrayBinding) binding;
			UnresolvedType componentType = fromBinding(aBinding.leafComponentType);
			return UnresolvedType.makeArray(componentType, aBinding.dimensions);
		}

		if (binding instanceof WildcardBinding) {
			WildcardBinding eWB = (WildcardBinding) binding;
			// Repair the bound
			// e.g. If the bound for the wildcard is a typevariable, e.g. '? extends E' then
			// the type variable in the unresolvedtype will be correct only in name. In that
			// case let's set it correctly based on the one in the eclipse WildcardBinding
			UnresolvedType theBound = null;
			if (eWB.bound instanceof TypeVariableBinding) {
				theBound = fromTypeVariableBinding((TypeVariableBinding) eWB.bound);
			} else {
				theBound = fromBinding(eWB.bound);
			}
			// if (eWB.boundKind == WildCard.SUPER) {
			//
			// }
			WildcardedUnresolvedType theType = (WildcardedUnresolvedType) TypeFactory.createTypeFromSignature(CharOperation
					.charToString(eWB.genericTypeSignature()));
			// if (theType.isGenericWildcard() && theType.isSuper()) theType.setLowerBound(theBound);
			// if (theType.isGenericWildcard() && theType.isExtends()) theType.setUpperBound(theBound);
			return theType;
		}

		if (binding instanceof ParameterizedTypeBinding) {
			if (binding instanceof RawTypeBinding) {
				// special case where no parameters are specified!
				return UnresolvedType.forRawTypeName(getName(binding));
			}
			ParameterizedTypeBinding ptb = (ParameterizedTypeBinding) binding;

			UnresolvedType[] arguments = null;

			if (ptb.arguments != null) { // null can mean this is an inner type of a Parameterized Type with no bounds of its own
				// (pr100227)
				arguments = new UnresolvedType[ptb.arguments.length];
				for (int i = 0; i < arguments.length; i++) {
					arguments[i] = fromBinding(ptb.arguments[i]);
				}
			}

			String baseTypeSignature = null;

			ResolvedType baseType = getWorld().resolve(UnresolvedType.forName(getName(binding)), true);
			if (!baseType.isMissing()) {
				// can legitimately be missing if a bound refers to a type we haven't added to the world yet...
				// pr168044 - sometimes (whilst resolving types) we are working with 'half finished' types and so (for example) the
				// underlying generic type for a raw type hasnt been set yet
				// if (!baseType.isGenericType() && arguments!=null) baseType = baseType.getGenericType();
// pr384398 - secondary testcase in 1.7.1 tests - this needs work as this code
// currently discards the parameterization on the outer type, which is important info
//				ReferenceBinding enclosingTypeBinding = ptb.enclosingType();
//				if (enclosingTypeBinding!=null) {
//					UnresolvedType ttt = fromBinding(enclosingTypeBinding);
//					baseTypeSignature = ttt.getSignature();
//					baseTypeSignature= baseTypeSignature.substring(0,baseTypeSignature.length()-1);
//					baseTypeSignature = baseTypeSignature + "."+new String(ptb.sourceName)+";";
//				} else {			
					baseTypeSignature = baseType.getErasureSignature();
//				}
			} else {
				baseTypeSignature = UnresolvedType.forName(getName(binding)).getSignature();
			}

			// Create an unresolved parameterized type. We can't create a resolved one as the
			// act of resolution here may cause recursion problems since the parameters may
			// be type variables that we haven't fixed up yet.
			if (arguments == null) {
				arguments = new UnresolvedType[0];
				// for pr384398
				if (!hasAnyArguments(ptb)) {
					return UnresolvedType.forRawTypeName(getName(binding));
				}
			}
			// StringBuffer parameterizedSig = new StringBuffer();
			// parameterizedSig.append(ResolvedType.PARAMETERIZED_TYPE_IDENTIFIER);
			//
			// // String parameterizedSig = new
			// StringBuffer().append(ResolvedType.PARAMETERIZED_TYPE_IDENTIFIER).append(CharOperation
			// .charToString(binding.genericTypeSignature()).substring(1)).toString();
			// return TypeFactory.createUnresolvedParameterizedType(parameterizedSig,baseTypeSignature,arguments);
			return TypeFactory.createUnresolvedParameterizedType(baseTypeSignature, arguments);
		}

		// Convert the source type binding for a generic type into a generic UnresolvedType
		// notice we can easily determine the type variables from the eclipse object
		// and we can recover the generic signature from it too - so we pass those
		// to the forGenericType() method.
		if (binding.isGenericType() && !binding.isParameterizedType() && !binding.isRawType()) {
			TypeVariableBinding[] tvbs = binding.typeVariables();
			TypeVariable[] tVars = new TypeVariable[tvbs.length];
			for (int i = 0; i < tvbs.length; i++) {
				TypeVariableBinding eclipseV = tvbs[i];
				tVars[i] = ((TypeVariableReference) fromTypeVariableBinding(eclipseV)).getTypeVariable();
			}
			// TODO asc generics - temporary guard....
			if (!(binding instanceof SourceTypeBinding)) {
				throw new RuntimeException("Cant get the generic sig for " + binding.debugName());
			}
			return UnresolvedType.forGenericType(getName(binding), tVars,
					CharOperation.charToString(((SourceTypeBinding) binding).genericSignature()));
		}

		// LocalTypeBinding have a name $Local$, we can get the real name by using the signature....
		if (binding instanceof LocalTypeBinding) {
			LocalTypeBinding ltb = (LocalTypeBinding) binding;
			if (ltb.constantPoolName() != null && ltb.constantPoolName().length > 0) {
				return UnresolvedType.forSignature(new String(binding.signature()));
			} else {
				// we're reporting a problem and don't have a resolved name for an
				// anonymous local type yet, report the issue on the enclosing type
				return UnresolvedType.forSignature(new String(ltb.enclosingType.signature()));
			}
		}

		// was: UnresolvedType.forName(getName(binding));
		UnresolvedType ut = UnresolvedType.forSignature(new String(binding.signature()));
		return ut;
	}

	/**
	 * Search up a parameterized type binding for any arguments at any level.
	 */
	private boolean hasAnyArguments(ParameterizedTypeBinding ptb) {
		if (ptb.arguments!=null && ptb.arguments.length>0) {
			return true;
		}
		ReferenceBinding enclosingType = ptb.enclosingType();
		if (enclosingType instanceof ParameterizedTypeBinding) {
			return hasAnyArguments((ParameterizedTypeBinding)enclosingType);
		} else {
			return false;
		}
	}

	/**
	 * Some type variables refer to themselves recursively, this enables us to avoid recursion problems.
	 */
	private static Map<TypeVariableBinding,UnresolvedType> typeVariableBindingsInProgress = new HashMap<TypeVariableBinding,UnresolvedType>();

	/**
	 * Convert from the eclipse form of type variable (TypeVariableBinding) to the AspectJ form (TypeVariable).
	 */
	private UnresolvedType fromTypeVariableBinding(TypeVariableBinding aTypeVariableBinding) {
		// first, check for recursive call to this method for the same tvBinding
		if (typeVariableBindingsInProgress.containsKey(aTypeVariableBinding)) {
			return typeVariableBindingsInProgress.get(aTypeVariableBinding);
		}

		// Check if its a type variable binding that we need to recover to an alias...
		if (typeVariablesForAliasRecovery != null) {
			String aliasname = typeVariablesForAliasRecovery.get(aTypeVariableBinding);
			if (aliasname != null) {
				UnresolvedTypeVariableReferenceType ret = new UnresolvedTypeVariableReferenceType();
				ret.setTypeVariable(new TypeVariable(aliasname));
				return ret;
			}
		}

		if (typeVariablesForThisMember.containsKey(new String(aTypeVariableBinding.sourceName))) {
			return typeVariablesForThisMember.get(new String(aTypeVariableBinding.sourceName));
		}

		// Create the UnresolvedTypeVariableReferenceType for the type variable
		String name = CharOperation.charToString(aTypeVariableBinding.sourceName());

		UnresolvedTypeVariableReferenceType ret = new UnresolvedTypeVariableReferenceType();
		typeVariableBindingsInProgress.put(aTypeVariableBinding, ret);

		TypeVariable tv = new TypeVariable(name);
		ret.setTypeVariable(tv);
		// Dont set any bounds here, you'll get in a recursive mess
		// TODO -- what about lower bounds??
		UnresolvedType superclassType = fromBinding(aTypeVariableBinding.superclass());
		UnresolvedType[] superinterfaces = null;
		if (aTypeVariableBinding == null || aTypeVariableBinding.superInterfaces == null) {
			// sign of another bug that will be reported elsewhere
			superinterfaces = UnresolvedType.NONE;
		} else {
			superinterfaces = new UnresolvedType[aTypeVariableBinding.superInterfaces.length];
			for (int i = 0; i < superinterfaces.length; i++) {
				superinterfaces[i] = fromBinding(aTypeVariableBinding.superInterfaces[i]);
			}
		}
		tv.setSuperclass(superclassType);
		tv.setAdditionalInterfaceBounds(superinterfaces);
		tv.setRank(aTypeVariableBinding.rank);
		if (aTypeVariableBinding.declaringElement instanceof MethodBinding) {
			tv.setDeclaringElementKind(TypeVariable.METHOD);
			// tv.setDeclaringElement(fromBinding((MethodBinding)aTypeVariableBinding.declaringElement);
		} else {
			tv.setDeclaringElementKind(TypeVariable.TYPE);
			// // tv.setDeclaringElement(fromBinding(aTypeVariableBinding.declaringElement));
		}
		if (aTypeVariableBinding.declaringElement instanceof MethodBinding) {
			typeVariablesForThisMember.put(new String(aTypeVariableBinding.sourceName), ret);
		}
		typeVariableBindingsInProgress.remove(aTypeVariableBinding);
		return ret;
	}

	public UnresolvedType[] fromBindings(TypeBinding[] bindings) {
		if (bindings == null) {
			return UnresolvedType.NONE;
		}
		int len = bindings.length;
		UnresolvedType[] ret = new UnresolvedType[len];
		for (int i = 0; i < len; i++) {
			ret[i] = fromBinding(bindings[i]);
		}
		return ret;
	}

	public static ASTNode astForLocation(IHasPosition location) {
		return new EmptyStatement(location.getStart(), location.getEnd());
	}

	public List<DeclareParents> getDeclareParents() {
		return getWorld().getDeclareParents();
	}

	public List<DeclareAnnotation> getDeclareAnnotationOnTypes() {
		return getWorld().getDeclareAnnotationOnTypes();
	}

	public List<DeclareAnnotation> getDeclareAnnotationOnFields() {
		return getWorld().getDeclareAnnotationOnFields();
	}

	public List<DeclareAnnotation> getDeclareAnnotationOnMethods() {
		return getWorld().getDeclareAnnotationOnMethods();
	}

	public boolean areTypeMungersFinished() {
		return finishedTypeMungers != null;
	}

	public void finishTypeMungers() {
		// make sure that type mungers are
		List<ConcreteTypeMunger> ret = new ArrayList<ConcreteTypeMunger>();
		List<ConcreteTypeMunger> baseTypeMungers = getWorld().getCrosscuttingMembersSet().getTypeMungers();

		// XXX by Andy: why do we mix up the mungers here? it means later we know about two sets
		// and the late ones are a subset of the complete set? (see pr114436)
		// XXX by Andy removed this line finally, see pr141956
		// baseTypeMungers.addAll(getWorld().getCrosscuttingMembersSet().getLateTypeMungers());
		debug_mungerCount = baseTypeMungers.size();

		for (ConcreteTypeMunger munger : baseTypeMungers) {
			EclipseTypeMunger etm = makeEclipseTypeMunger(munger);
			if (etm != null) {
				if (munger.getMunger().getKind() == ResolvedTypeMunger.InnerClass) {
					ret.add(0, etm);
				} else {
					ret.add(etm);
				}
			}
		}
		for (ConcreteTypeMunger ctm: ret) {
			ResolvedMember rm = ctm.getSignature();
			if (rm!=null) {
				rm.resolve(this.getWorld());
			}
		}
		finishedTypeMungers = ret;
	}

	public EclipseTypeMunger makeEclipseTypeMunger(ConcreteTypeMunger concrete) {
		// System.err.println("make munger: " + concrete);
		// !!! can't do this if we want incremental to work right
		// if (concrete instanceof EclipseTypeMunger) return (EclipseTypeMunger)concrete;
		// System.err.println("   was not eclipse");

		if (concrete.getMunger() != null && EclipseTypeMunger.supportsKind(concrete.getMunger().getKind())) {
			AbstractMethodDeclaration method = null;
			if (concrete instanceof EclipseTypeMunger) {
				method = ((EclipseTypeMunger) concrete).getSourceMethod();
			}
			EclipseTypeMunger ret = new EclipseTypeMunger(this, concrete.getMunger(), concrete.getAspectType(), method);
			if (ret.getSourceLocation() == null) {
				ret.setSourceLocation(concrete.getSourceLocation());
			}
			return ret;
		} else {
			return null;
		}
	}

	public List<ConcreteTypeMunger> getTypeMungers() {
		// ??? assert finishedTypeMungers != null
		return finishedTypeMungers;
	}

	public ResolvedMemberImpl makeResolvedMember(MethodBinding binding) {
		return makeResolvedMember(binding, binding.declaringClass);
	}

	public ResolvedMemberImpl makeResolvedMember(MethodBinding binding, Shadow.Kind shadowKind) {
		MemberKind memberKind = binding.isConstructor() ? Member.CONSTRUCTOR : Member.METHOD;
		if (shadowKind == Shadow.AdviceExecution) {
			memberKind = Member.ADVICE;
		}
		return makeResolvedMember(binding, binding.declaringClass, memberKind);
	}

	/**
	 * Conversion from a methodbinding (eclipse) to a resolvedmember (aspectj) is now done in the scope of some type variables.
	 * Before converting the parts of a methodbinding (params, return type) we store the type variables in this structure, then
	 * should any component of the method binding refer to them, we grab them from the map.
	 */
	private final Map<String,UnresolvedType> typeVariablesForThisMember = new HashMap<String, UnresolvedType>();

	/**
	 * This is a map from typevariablebindings (eclipsey things) to the names the user originally specified in their ITD. For
	 * example if the target is 'interface I<N extends Number> {}' and the ITD was 'public void I<X>.m(List<X> lxs) {}' then this
	 * map would contain a pointer from the eclipse type 'N extends Number' to the letter 'X'.
	 */
	private Map<TypeVariableBinding, String> typeVariablesForAliasRecovery;

	/**
	 * Construct a resolvedmember from a methodbinding. The supplied map tells us about any typevariablebindings that replaced
	 * typevariables whilst the compiler was resolving types - this only happens if it is a generic itd that shares type variables
	 * with its target type.
	 */
	public ResolvedMemberImpl makeResolvedMemberForITD(MethodBinding binding, TypeBinding declaringType, Map<TypeVariableBinding,String> recoveryAliases) {
		ResolvedMemberImpl result = null;
		try {
			typeVariablesForAliasRecovery = recoveryAliases;
			result = makeResolvedMember(binding, declaringType);
		} finally {
			typeVariablesForAliasRecovery = null;
		}
		return result;
	}

	public ResolvedMemberImpl makeResolvedMember(MethodBinding binding, TypeBinding declaringType) {
		return makeResolvedMember(binding, declaringType, binding.isConstructor() ? Member.CONSTRUCTOR : Member.METHOD);
	}

	public ResolvedMemberImpl makeResolvedMember(MethodBinding binding, TypeBinding declaringType, MemberKind memberKind) {
		// System.err.println("member for: " + binding + ", " + new String(binding.declaringClass.sourceName));

		// Convert the type variables and store them
		UnresolvedType[] ajTypeRefs = null;
		typeVariablesForThisMember.clear();
		// This is the set of type variables available whilst building the resolved member...
		if (binding.typeVariables != null) {
			ajTypeRefs = new UnresolvedType[binding.typeVariables.length];
			for (int i = 0; i < binding.typeVariables.length; i++) {
				ajTypeRefs[i] = fromBinding(binding.typeVariables[i]);
				typeVariablesForThisMember.put(new String(binding.typeVariables[i].sourceName),/*
																								 * new
																								 * Integer(binding.typeVariables[
																								 * i].rank),
																								 */ajTypeRefs[i]);
			}
		}

		// AMC these next two lines shouldn't be needed once we sort out generic types properly in the world map
		ResolvedType realDeclaringType = world.resolve(fromBinding(declaringType));
		if (realDeclaringType.isRawType()) {
			realDeclaringType = realDeclaringType.getGenericType();
		}
		ResolvedMemberImpl ret = new EclipseResolvedMember(binding, memberKind, realDeclaringType, binding.modifiers,
				fromBinding(binding.returnType), new String(binding.selector), fromBindings(binding.parameters),
				fromBindings(binding.thrownExceptions), this);
		if (binding.isVarargs()) {
			ret.setVarargsMethod();
		}
		if (ajTypeRefs != null) {
			TypeVariable[] tVars = new TypeVariable[ajTypeRefs.length];
			for (int i = 0; i < ajTypeRefs.length; i++) {
				tVars[i] = ((TypeVariableReference) ajTypeRefs[i]).getTypeVariable();
			}
			ret.setTypeVariables(tVars);
		}
		typeVariablesForThisMember.clear();
		ret.resolve(world);
		return ret;
	}

	public ResolvedMember makeResolvedMember(FieldBinding binding) {
		return makeResolvedMember(binding, binding.declaringClass);
	}

	public ResolvedMember makeResolvedMember(FieldBinding binding, TypeBinding receiverType) {
		// AMC these next two lines shouldn't be needed once we sort out generic types properly in the world map
		ResolvedType realDeclaringType = world.resolve(fromBinding(receiverType));
		if (realDeclaringType.isRawType()) {
			realDeclaringType = realDeclaringType.getGenericType();
		}
		ResolvedMemberImpl ret = new EclipseResolvedMember(binding, Member.FIELD, realDeclaringType, binding.modifiers,
				world.resolve(fromBinding(binding.type)), new String(binding.name), UnresolvedType.NONE);
		return ret;
	}

	public TypeBinding makeTypeBinding(UnresolvedType typeX) {

		TypeBinding ret = null;

		// looking up type variables can get us into trouble
		if (!typeX.isTypeVariableReference() && !isParameterizedWithTypeVariables(typeX)) {
			if (typeX.isRawType()) {
				ret = rawTypeXToBinding.get(typeX);
			} else {
				ret = typexToBinding.get(typeX);
			}
		}

		if (ret == null) {
			ret = makeTypeBinding1(typeX);
			if (ret != null) {// && !(ret instanceof ProblemReferenceBinding)) {
				if (!(typeX instanceof BoundedReferenceType) && !(typeX instanceof UnresolvedTypeVariableReferenceType)) {
					if (typeX.isRawType()) {
						rawTypeXToBinding.put(typeX, ret);
					} else {
						typexToBinding.put(typeX, ret);
					}
				}
			}
		}
		return ret;
	}

	// return true if this is type variables are in the type arguments
	private boolean isParameterizedWithTypeVariables(UnresolvedType typeX) {
		if (!typeX.isParameterizedType()) {
			return false;
		}
		UnresolvedType[] typeArguments = typeX.getTypeParameters();
		if (typeArguments != null) {
			for (int i = 0; i < typeArguments.length; i++) {
				if (typeArguments[i].isTypeVariableReference()) {
					return true;
				}
			}
		}
		return false;
	}

	// When converting a parameterized type from our world to the eclipse world, these get set so that
	// resolution of the type parameters may known in what context it is occurring (pr114744)
	private ReferenceBinding baseTypeForParameterizedType;
	private int indexOfTypeParameterBeingConverted;

	private TypeBinding makeTypeBinding1(UnresolvedType typeX) {
		if (typeX.isPrimitiveType()) {
			if (typeX.equals(UnresolvedType.BOOLEAN)) {
				return TypeBinding.BOOLEAN;
			}
			if (typeX.equals(UnresolvedType.BYTE)) {
				return TypeBinding.BYTE;
			}
			if (typeX.equals(UnresolvedType.CHAR)) {
				return TypeBinding.CHAR;
			}
			if (typeX.equals(UnresolvedType.DOUBLE)) {
				return TypeBinding.DOUBLE;
			}
			if (typeX.equals(UnresolvedType.FLOAT)) {
				return TypeBinding.FLOAT;
			}
			if (typeX.equals(UnresolvedType.INT)) {
				return TypeBinding.INT;
			}
			if (typeX.equals(UnresolvedType.LONG)) {
				return TypeBinding.LONG;
			}
			if (typeX.equals(UnresolvedType.SHORT)) {
				return TypeBinding.SHORT;
			}
			if (typeX.equals(UnresolvedType.VOID)) {
				return TypeBinding.VOID;
			}
			throw new RuntimeException("weird primitive type " + typeX);
		} else if (typeX.isArray()) {
			int dim = 0;
			while (typeX.isArray()) {
				dim++;
				typeX = typeX.getComponentType();
			}
			return lookupEnvironment.createArrayType(makeTypeBinding(typeX), dim);
		} else if (typeX.isParameterizedType()) {
			// Converting back to a binding from a UnresolvedType
			UnresolvedType[] typeParameters = typeX.getTypeParameters();
			ReferenceBinding baseTypeBinding = lookupBinding(typeX.getBaseName());
			TypeBinding[] argumentBindings = new TypeBinding[typeParameters.length];
			baseTypeForParameterizedType = baseTypeBinding;
			for (int i = 0; i < argumentBindings.length; i++) {
				indexOfTypeParameterBeingConverted = i;
				argumentBindings[i] = makeTypeBinding(typeParameters[i]);
			}
			indexOfTypeParameterBeingConverted = 0;
			baseTypeForParameterizedType = null;
			ParameterizedTypeBinding ptb = lookupEnvironment.createParameterizedType(baseTypeBinding, argumentBindings,
					baseTypeBinding.enclosingType());
			return ptb;
		} else if (typeX.isTypeVariableReference()) {
			// return makeTypeVariableBinding((TypeVariableReference)typeX);
			return makeTypeVariableBindingFromAJTypeVariable(((TypeVariableReference) typeX).getTypeVariable());
		} else if (typeX.isRawType()) {
			ReferenceBinding baseTypeBinding = lookupBinding(typeX.getBaseName());
			RawTypeBinding rtb = lookupEnvironment.createRawType(baseTypeBinding, baseTypeBinding.enclosingType());
			return rtb;
		} else if (typeX.isGenericWildcard()) {
			if (typeX instanceof WildcardedUnresolvedType) {
				WildcardedUnresolvedType wut = (WildcardedUnresolvedType) typeX;
				int boundkind = Wildcard.UNBOUND;
				TypeBinding bound = null;
				if (wut.isExtends()) {
					boundkind = Wildcard.EXTENDS;
					bound = makeTypeBinding(wut.getUpperBound());
				} else if (wut.isSuper()) {
					boundkind = Wildcard.SUPER;
					bound = makeTypeBinding(wut.getLowerBound());
				}
				TypeBinding[] otherBounds = null;
				// TODO 2 ought to support extra bounds for WildcardUnresolvedType
				// if (wut.getAdditionalBounds()!=null && wut.getAdditionalBounds().length!=0) otherBounds =
				// makeTypeBindings(wut.getAdditionalBounds());
				WildcardBinding wb = lookupEnvironment.createWildcard(baseTypeForParameterizedType,
						indexOfTypeParameterBeingConverted, bound, otherBounds, boundkind);
				return wb;
			} else if (typeX instanceof BoundedReferenceType) {
				// translate from boundedreferencetype to WildcardBinding
				BoundedReferenceType brt = (BoundedReferenceType) typeX;
				// Work out 'kind' for the WildcardBinding
				int boundkind = Wildcard.UNBOUND;
				TypeBinding bound = null;
				if (brt.isExtends()) {
					boundkind = Wildcard.EXTENDS;
					bound = makeTypeBinding(brt.getUpperBound());
				} else if (brt.isSuper()) {
					boundkind = Wildcard.SUPER;
					bound = makeTypeBinding(brt.getLowerBound());
				}
				TypeBinding[] otherBounds = null;
				if (brt.getAdditionalBounds() != null && brt.getAdditionalBounds().length != 0) {
					otherBounds = makeTypeBindings(brt.getAdditionalBounds());
				}
				WildcardBinding wb = lookupEnvironment.createWildcard(baseTypeForParameterizedType,
						indexOfTypeParameterBeingConverted, bound, otherBounds, boundkind);
				return wb;
			} else {
				throw new BCException("This type " + typeX + " (class " + typeX.getClass().getName()
						+ ") should not be claiming to be a wildcard!");
			}
		} else {
			return lookupBinding(typeX.getName());
		}
	}

	private ReferenceBinding lookupBinding(String sname) {
		char[][] name = CharOperation.splitOn('.', sname.toCharArray());
		ReferenceBinding rb = lookupEnvironment.getType(name);
		if (rb == null && !sname.equals(UnresolvedType.MISSING_NAME)) {
			return new ProblemReferenceBinding(name, null, ProblemReasons.NotFound);
		}
		return rb;
	}

	public TypeBinding[] makeTypeBindings(UnresolvedType[] types) {
		int len = types.length;
		TypeBinding[] ret = new TypeBinding[len];

		for (int i = 0; i < len; i++) {
			ret[i] = makeTypeBinding(types[i]);
		}
		return ret;
	}

	// just like the code above except it returns an array of ReferenceBindings
	private ReferenceBinding[] makeReferenceBindings(UnresolvedType[] types) {
		int len = types.length;
		ReferenceBinding[] ret = new ReferenceBinding[len];

		for (int i = 0; i < len; i++) {
			ret[i] = (ReferenceBinding) makeTypeBinding(types[i]);
		}
		return ret;
	}

	// field related

	public FieldBinding makeFieldBinding(NewFieldTypeMunger nftm) {
		return internalMakeFieldBinding(nftm.getSignature(), nftm.getTypeVariableAliases());
	}

	/**
	 * Convert a resolvedmember into an eclipse field binding
	 */
	public FieldBinding makeFieldBinding(ResolvedMember member, List<String> aliases) {
		return internalMakeFieldBinding(member, aliases);
	}

	/**
	 * Convert a resolvedmember into an eclipse field binding
	 */
	public FieldBinding makeFieldBinding(ResolvedMember member) {
		return internalMakeFieldBinding(member, null);
	}

	// OPTIMIZE tidy this up, must be able to optimize for the synthetic case, if we passed in the binding for the declaring type,
	// that would make things easier
	/**
	 * Build a new Eclipse SyntheticFieldBinding for an AspectJ ResolvedMember.
	 */
	public SyntheticFieldBinding createSyntheticFieldBinding(SourceTypeBinding owningType, ResolvedMember member) {
		SyntheticFieldBinding sfb = new SyntheticFieldBinding(member.getName().toCharArray(),
				makeTypeBinding(member.getReturnType()), member.getModifiers() | Flags.AccSynthetic, owningType,
				Constant.NotAConstant, -1); // index
		// filled in
		// later
		owningType.addSyntheticField(sfb);
		return sfb;
	}

	/**
	 * Take a normal AJ member and convert it into an eclipse fieldBinding. Taking into account any aliases that it may include due
	 * to being a generic itd. Any aliases are put into the typeVariableToBinding map so that they will be substituted as
	 * appropriate in the returned fieldbinding.
	 */
	public FieldBinding internalMakeFieldBinding(ResolvedMember member, List<String> aliases) {
		typeVariableToTypeBinding.clear();

		ReferenceBinding declaringType = (ReferenceBinding) makeTypeBinding(member.getDeclaringType());

		// If there are aliases, place them in the map
		if (aliases != null && aliases.size() > 0 && declaringType.typeVariables() != null
				&& declaringType.typeVariables().length != 0) {
			int i = 0;
			for (String element: aliases) {
//			for (Iterator iter = aliases.iterator(); iter.hasNext();) {
//				String element = (String) iter.next();
				typeVariableToTypeBinding.put(element, declaringType.typeVariables()[i++]);
			}
		}

		currentType = declaringType;

		FieldBinding fb = null;
		if (member.getName().startsWith(NameMangler.PREFIX)) {
			fb = new SyntheticFieldBinding(member.getName().toCharArray(), makeTypeBinding(member.getReturnType()),
					member.getModifiers() | Flags.AccSynthetic, currentType, Constant.NotAConstant, -1); // index filled in later
		} else {
			fb = new FieldBinding(member.getName().toCharArray(), makeTypeBinding(member.getReturnType()), member.getModifiers(),
					currentType, Constant.NotAConstant);
		}
		typeVariableToTypeBinding.clear();
		currentType = null;

		if (member.getName().startsWith(NameMangler.PREFIX)) {
			fb.modifiers |= Flags.AccSynthetic;
		}
		return fb;
	}

	private ReferenceBinding currentType = null;

	// method binding related

	public MethodBinding makeMethodBinding(NewMethodTypeMunger nmtm) {
		return internalMakeMethodBinding(nmtm.getSignature(), nmtm.getTypeVariableAliases());
	}

	/**
	 * Convert a resolvedmember into an eclipse method binding.
	 */
	public MethodBinding makeMethodBinding(ResolvedMember member, List<String> aliases) {
		return internalMakeMethodBinding(member, aliases);
	}

	/**
	 * Creates a method binding for a resolvedmember taking into account type variable aliases - this variant can take an
	 * aliasTargetType and should be used when the alias target type cannot be retrieved from the resolvedmember.
	 */
	public MethodBinding makeMethodBinding(ResolvedMember member, List aliases, UnresolvedType aliasTargetType) {
		return internalMakeMethodBinding(member, aliases, aliasTargetType);
	}

	/**
	 * Convert a resolvedmember into an eclipse method binding.
	 */
	public MethodBinding makeMethodBinding(ResolvedMember member) {
		return internalMakeMethodBinding(member, null); // there are no aliases
	}

	public MethodBinding internalMakeMethodBinding(ResolvedMember member, List<String> aliases) {
		return internalMakeMethodBinding(member, aliases, member.getDeclaringType());
	}

	/**
	 * Take a normal AJ member and convert it into an eclipse methodBinding. Taking into account any aliases that it may include due
	 * to being a generic ITD. Any aliases are put into the typeVariableToBinding map so that they will be substituted as
	 * appropriate in the returned methodbinding
	 */
	public MethodBinding internalMakeMethodBinding(ResolvedMember member, List<String> aliases, UnresolvedType aliasTargetType) {
		typeVariableToTypeBinding.clear();
		TypeVariableBinding[] tvbs = null;

		if (member.getTypeVariables() != null) {
			if (member.getTypeVariables().length == 0) {
				tvbs = Binding.NO_TYPE_VARIABLES;
			} else {
				tvbs = makeTypeVariableBindingsFromAJTypeVariables(member.getTypeVariables());
				// QQQ do we need to bother fixing up the declaring element here?
			}
		}

		ReferenceBinding declaringType = (ReferenceBinding) makeTypeBinding(member.getDeclaringType());

		// If there are aliases, place them in the map
		if (aliases != null && aliases.size() != 0
				
				// Not sure what this check is trying to check for?
				// In latest JDT (2-Feb-2018) there seem to be some code (in LookupEnvironment ~860 and
				// TypeSystem ~340) related to avoiding raw'ifying the enclosing type if the type to
				// be rawed is static.  These changes cause these checks to fail and the sophisticated
				// test variant V fails.  It checks declaring type typevariables and then
				// uses aliasTargetType ... I'm switching it to check aliasTargetType
				
//				&& declaringType.typeVariables() != null
//				&& declaringType.typeVariables().length != 0
				&& aliasTargetType != null
				&& aliasTargetType.getTypeVariables() != null
				&& aliasTargetType.getTypeVariables().length!=0
				) {
			int i = 0;
			ReferenceBinding aliasTarget = (ReferenceBinding) makeTypeBinding(aliasTargetType);
			if (aliasTarget.isRawType()) {
				aliasTarget = ((RawTypeBinding) aliasTarget).genericType();
			}
			for (String alias: aliases) {
				typeVariableToTypeBinding.put(alias, aliasTarget.typeVariables()[i++]);
			}
		}

		currentType = declaringType;
		MethodBinding mb = new MethodBinding(member.getModifiers(), member.getName().toCharArray(),
				makeTypeBinding(member.getReturnType()),
				makeTypeBindings(member.getParameterTypes()),
				makeReferenceBindings(member.getExceptions()), declaringType);

		if (tvbs != null) {
			mb.typeVariables = tvbs;
		}
		typeVariableToTypeBinding.clear();
		currentType = null;

		if (NameMangler.isSyntheticMethod(member.getName(), true)) {
			mb.modifiers |= Flags.AccSynthetic;
		}

		return mb;
	}

	/**
	 * Convert a bunch of type variables in one go, from AspectJ form to Eclipse form.
	 */
	// private TypeVariableBinding[] makeTypeVariableBindings(UnresolvedType[] typeVariables) {
	// int len = typeVariables.length;
	// TypeVariableBinding[] ret = new TypeVariableBinding[len];
	// for (int i = 0; i < len; i++) {
	// ret[i] = makeTypeVariableBinding((TypeVariableReference)typeVariables[i]);
	// }
	// return ret;
	// }
	private TypeVariableBinding[] makeTypeVariableBindingsFromAJTypeVariables(TypeVariable[] typeVariables) {
		int len = typeVariables.length;
		TypeVariableBinding[] ret = new TypeVariableBinding[len];
		for (int i = 0; i < len; i++) {
			ret[i] = makeTypeVariableBindingFromAJTypeVariable(typeVariables[i]);
		}
		return ret;
	}

	// only accessed through private methods in this class. Ensures all type variables we encounter
	// map back to the same type binding - this is important later when Eclipse code is processing
	// a methodbinding trying to come up with possible bindings for the type variables.
	// key is currently the name of the type variable...is that ok?
	private final Map<String,TypeVariableBinding> typeVariableToTypeBinding = new HashMap<String,TypeVariableBinding>();

	// /**
	// * Converts from an TypeVariableReference to a TypeVariableBinding. A TypeVariableReference
	// * in AspectJ world holds a TypeVariable and it is this type variable that is converted
	// * to the TypeVariableBinding.
	// */
	// private TypeVariableBinding makeTypeVariableBinding(TypeVariableReference tvReference) {
	// TypeVariable tv = tvReference.getTypeVariable();
	// TypeVariableBinding tvBinding = (TypeVariableBinding)typeVariableToTypeBinding.get(tv.getName());
	// if (currentType!=null) {
	// TypeVariableBinding tvb = currentType.getTypeVariable(tv.getName().toCharArray());
	// if (tvb!=null) return tvb;
	// }
	// if (tvBinding==null) {
	// Binding declaringElement = null;
	// // this will cause an infinite loop or NPE... not required yet luckily.
	// // if (tVar.getDeclaringElement() instanceof Member) {
	// // declaringElement = makeMethodBinding((ResolvedMember)tVar.getDeclaringElement());
	// // } else {
	// // declaringElement = makeTypeBinding((UnresolvedType)tVar.getDeclaringElement());
	// // }
	//
	// tvBinding = new TypeVariableBinding(tv.getName().toCharArray(),null,tv.getRank());
	//
	// typeVariableToTypeBinding.put(tv.getName(),tvBinding);
	// tvBinding.superclass=(ReferenceBinding)makeTypeBinding(tv.getUpperBound());
	// tvBinding.firstBound=makeTypeBinding(tv.getFirstBound());
	// if (tv.getAdditionalInterfaceBounds()==null) {
	// tvBinding.superInterfaces=TypeVariableBinding.NO_SUPERINTERFACES;
	// } else {
	// TypeBinding tbs[] = makeTypeBindings(tv.getAdditionalInterfaceBounds());
	// ReferenceBinding[] rbs= new ReferenceBinding[tbs.length];
	// for (int i = 0; i < tbs.length; i++) {
	// rbs[i] = (ReferenceBinding)tbs[i];
	// }
	// tvBinding.superInterfaces=rbs;
	// }
	// }
	// return tvBinding;
	// }

	private TypeVariableBinding makeTypeVariableBindingFromAJTypeVariable(TypeVariable tv) {
		TypeVariableBinding tvBinding = typeVariableToTypeBinding.get(tv.getName());
		if (currentType != null) {
			TypeVariableBinding tvb = currentType.getTypeVariable(tv.getName().toCharArray());
			if (tvb != null) {
				return tvb;
			}
		}
		if (tvBinding == null) {
			Binding declaringElement = null;
			// this will cause an infinite loop or NPE... not required yet luckily.
			// if (tVar.getDeclaringElement() instanceof Member) {
			// declaringElement = makeMethodBinding((ResolvedMember)tVar.getDeclaringElement());
			// } else {
			// declaringElement = makeTypeBinding((UnresolvedType)tVar.getDeclaringElement());
			// }
			tvBinding = new TypeVariableBinding(tv.getName().toCharArray(), declaringElement, tv.getRank(),this.lookupEnvironment);
			typeVariableToTypeBinding.put(tv.getName(), tvBinding);

			if (tv.getSuperclass() != null
					&& (!tv.getSuperclass().getSignature().equals("Ljava/lang/Object;") || tv.getSuperInterfaces() != null)) {
				tvBinding.superclass = (ReferenceBinding) makeTypeBinding(tv.getSuperclass());
			}
			tvBinding.firstBound = makeTypeBinding(tv.getFirstBound());
			if (tv.getSuperInterfaces() == null) {
				tvBinding.superInterfaces = TypeVariableBinding.NO_SUPERINTERFACES;
			} else {
				TypeBinding tbs[] = makeTypeBindings(tv.getSuperInterfaces());
				ReferenceBinding[] rbs = new ReferenceBinding[tbs.length];
				for (int i = 0; i < tbs.length; i++) {
					rbs[i] = (ReferenceBinding) tbs[i];
				}
				tvBinding.superInterfaces = rbs;
			}
		}
		return tvBinding;
	}

	public MethodBinding makeMethodBindingForCall(Member member) {
		return new MethodBinding(member.getModifiers() & ~Modifier.INTERFACE, member.getName().toCharArray(),
				makeTypeBinding(member.getReturnType()), makeTypeBindings(member.getParameterTypes()), new ReferenceBinding[0],
				(ReferenceBinding) makeTypeBinding(member.getDeclaringType()));
	}

	public void finishedCompilationUnit(CompilationUnitDeclaration unit) {
		if ((buildManager != null) && buildManager.doGenerateModel()) {
			AjBuildManager.getAsmHierarchyBuilder().buildStructureForCompilationUnit(unit, buildManager.getStructureModel(),
					buildManager.buildConfig);
		}
	}

	public void addTypeBinding(TypeBinding binding) {
		typexToBinding.put(fromBinding(binding), binding);
	}

	public void addTypeBindingAndStoreInWorld(TypeBinding binding) {
		UnresolvedType ut = fromBinding(binding);
		typexToBinding.put(ut, binding);
		world.lookupOrCreateName(ut);
	}

	public Shadow makeShadow(ASTNode location, ReferenceContext context) {
		return EclipseShadow.makeShadow(this, location, context);
	}

	public Shadow makeShadow(ReferenceContext context) {
		return EclipseShadow.makeShadow(this, (ASTNode) context, context);
	}

	public void addSourceTypeBinding(SourceTypeBinding binding, CompilationUnitDeclaration unit) {
		TypeDeclaration decl = binding.scope.referenceContext;

		// Deal with the raw/basic type to give us an entry in the world type map
		UnresolvedType unresolvedRawType = null;
		if (binding.isGenericType()) {
			unresolvedRawType = UnresolvedType.forRawTypeName(getName(binding));
		} else if (binding.isLocalType()) {
			LocalTypeBinding ltb = (LocalTypeBinding) binding;
			if (ltb.constantPoolName() != null && ltb.constantPoolName().length > 0) {
				unresolvedRawType = UnresolvedType.forSignature(new String(binding.signature()));
			} else {
				unresolvedRawType = UnresolvedType.forName(getName(binding));
			}
		} else {
			unresolvedRawType = UnresolvedType.forName(getName(binding));
		}

		ReferenceType resolvedRawType = getWorld().lookupOrCreateName(unresolvedRawType);

		// A type can change from simple > generic > simple across a set of compiles. We need
		// to ensure the entry in the typemap is promoted and demoted correctly. The call
		// to setGenericType() below promotes a simple to a raw. This call demotes it back
		// to simple
		// pr125405
		if (!binding.isRawType() && !binding.isGenericType() && resolvedRawType.getTypekind() == TypeKind.RAW) {
			resolvedRawType.demoteToSimpleType();
		}

		EclipseSourceType t = new EclipseSourceType(resolvedRawType, this, binding, decl, unit);

		// For generics, go a bit further - build a typex for the generic type
		// give it the same delegate and link it to the raw type
		if (binding.isGenericType()) {
			UnresolvedType unresolvedGenericType = fromBinding(binding); // fully aware of any generics info
			ResolvedType resolvedGenericType = world.resolve(unresolvedGenericType, true);
			ReferenceType complexName = null;
			if (!resolvedGenericType.isMissing()) {
				complexName = (ReferenceType) resolvedGenericType;
				complexName = complexName.getGenericType();
				if (complexName == null) {
					complexName = new ReferenceType(unresolvedGenericType, world);
				}
			} else {
				complexName = new ReferenceType(unresolvedGenericType, world);
			}
			resolvedRawType.setGenericType(complexName);
			complexName.setDelegate(t);
		}

		resolvedRawType.setDelegate(t);
		if (decl instanceof AspectDeclaration) {
			((AspectDeclaration) decl).typeX = resolvedRawType;
			((AspectDeclaration) decl).concreteName = t;
		}

		ReferenceBinding[] memberTypes = binding.memberTypes;
		for (int i = 0, length = memberTypes.length; i < length; i++) {
			addSourceTypeBinding((SourceTypeBinding) memberTypes[i], unit);
		}
	}

	// XXX this doesn't feel like it belongs here, but it breaks a hard dependency on
	// exposing AjBuildManager (needed by AspectDeclaration).
	public boolean isXSerializableAspects() {
		return xSerializableAspects;
	}

	public ResolvedMember fromBinding(MethodBinding binding) {
		return new ResolvedMemberImpl(Member.METHOD, fromBinding(binding.declaringClass), binding.modifiers,
				fromBinding(binding.returnType), CharOperation.charToString(binding.selector), fromBindings(binding.parameters));
	}

	public TypeVariableDeclaringElement fromBinding(Binding declaringElement) {
		if (declaringElement instanceof TypeBinding) {
			return fromBinding(((TypeBinding) declaringElement));
		} else {
			return fromBinding((MethodBinding) declaringElement);
		}
	}

	public void cleanup() {
		this.typexToBinding.clear();
		this.rawTypeXToBinding.clear();
		this.finishedTypeMungers = null;
	}

	public void minicleanup() {
		this.typexToBinding.clear();
		this.rawTypeXToBinding.clear();
	}

	public int getItdVersion() {
		return world.getItdVersion();
	}

}
