/*******************************************************************************
 * Copyright (c) 2000, 2018 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.aspectj.org.eclipse.jdt.internal.core;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Stack;

import org.eclipse.core.runtime.Assert;
import org.aspectj.org.eclipse.jdt.core.Flags;
import org.aspectj.org.eclipse.jdt.core.IAnnotation;
import org.aspectj.org.eclipse.jdt.core.ICompilationUnit;
import org.aspectj.org.eclipse.jdt.core.IJavaElement;
import org.aspectj.org.eclipse.jdt.core.IMemberValuePair;
import org.aspectj.org.eclipse.jdt.core.ITypeParameter;
import org.aspectj.org.eclipse.jdt.core.JavaModelException;
import org.aspectj.org.eclipse.jdt.core.Signature;
import org.aspectj.org.eclipse.jdt.core.compiler.CategorizedProblem;
import org.aspectj.org.eclipse.jdt.core.compiler.CharOperation;
import org.aspectj.org.eclipse.jdt.core.compiler.IProblem;
import org.aspectj.org.eclipse.jdt.internal.compiler.ISourceElementRequestor;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.Argument;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.ArrayInitializer;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.ClassLiteralAccess;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.Expression;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.ImportReference;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.Literal;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.MemberValuePair;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.NullLiteral;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.OperatorIds;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.UnaryExpression;
import org.aspectj.org.eclipse.jdt.internal.compiler.parser.Parser;
import org.aspectj.org.eclipse.jdt.internal.compiler.parser.RecoveryScanner;
import org.aspectj.org.eclipse.jdt.internal.compiler.util.HashtableOfObject;
import org.aspectj.org.eclipse.jdt.internal.compiler.util.HashtableOfObjectToInt;
import org.aspectj.org.eclipse.jdt.internal.core.util.ReferenceInfoAdapter;
import org.aspectj.org.eclipse.jdt.internal.core.util.Util;
/**
 * A requestor for the fuzzy parser, used to compute the children of an ICompilationUnit.
 */
@SuppressWarnings({ "rawtypes", "unchecked" })
public class CompilationUnitStructureRequestor extends ReferenceInfoAdapter implements ISourceElementRequestor {
	
	/**
	 * The handle to the compilation unit being parsed
	 */
	protected ICompilationUnit unit;

	/**
	 * The info object for the compilation unit being parsed
	 */
	protected CompilationUnitElementInfo unitInfo;

	/**
	 * The import container info - null until created
	 */
	protected ImportContainerInfo importContainerInfo = null;
	protected ImportContainer importContainer;

	/**
	 * Hashtable of children elements of the compilation unit.
	 * Children are added to the table as they are found by
	 * the parser. Keys are handles, values are corresponding
	 * info objects.
	 */
	protected Map newElements;

	/*
	 * A table from a handle (with occurenceCount == 1) to the current occurence count for this handle
	 */
	private HashtableOfObjectToInt occurenceCounts;

	/*
	 * A table to store the occurrence count of anonymous types. The key will be the handle to the
	 * enclosing type of the anonymous.
	 */
	private HashtableOfObjectToInt localOccurrenceCounts;

	/**
	 * Stack of parent scope info objects. The info on the
	 * top of the stack is the parent of the next element found.
	 * For example, when we locate a method, the parent info object
	 * will be the type the method is contained in.
	 */
	protected Stack infoStack;

	/*
	 * Map from info to of ArrayList of IJavaElement representing the children
	 * of the given info.
	 */
	protected HashMap children;

	/**
	 * Stack of parent handles, corresponding to the info stack. We
	 * keep both, since info objects do not have back pointers to
	 * handles.
	 */
	protected Stack handleStack;

	/**
	 * The number of references reported thus far. Used to
	 * expand the arrays of reference kinds and names.
	 */
	protected int referenceCount= 0;

	/**
	 * Problem requestor which will get notified of discovered problems
	 */
	protected boolean hasSyntaxErrors = false;

	/*
	 * The parser this requestor is using.
	 */
	protected Parser parser;

	protected HashtableOfObject fieldRefCache;
	protected HashtableOfObject messageRefCache;
	protected HashtableOfObject typeRefCache;
	protected HashtableOfObject unknownRefCache;

protected CompilationUnitStructureRequestor(ICompilationUnit unit, CompilationUnitElementInfo unitInfo, Map newElements) {
	this.unit = unit;
	this.unitInfo = unitInfo;
	this.newElements = newElements;
	this.occurenceCounts = new HashtableOfObjectToInt();
	this.localOccurrenceCounts = new HashtableOfObjectToInt(5);
}
/**
 * @see ISourceElementRequestor
 */
@Override
public void acceptImport(int declarationStart, int declarationEnd, int nameSourceStart, int nameSourceEnd, char[][] tokens, boolean onDemand, int modifiers) {
	JavaElement parentHandle= (JavaElement) this.handleStack.peek();
	if (!(parentHandle.getElementType() == IJavaElement.COMPILATION_UNIT)) {
		Assert.isTrue(false); // Should not happen
	}

	ICompilationUnit parentCU= (ICompilationUnit)parentHandle;
	//create the import container and its info
	if (this.importContainer == null) {
		this.importContainer = createImportContainer(parentCU);
		this.importContainerInfo = new ImportContainerInfo();
		Object parentInfo = this.infoStack.peek();
		addToChildren(parentInfo, this.importContainer);
		this.newElements.put(this.importContainer, this.importContainerInfo);
	}

	String elementName = JavaModelManager.getJavaModelManager().intern(new String(CharOperation.concatWith(tokens, '.')));
	ImportDeclaration handle = createImportDeclaration(this.importContainer, elementName, onDemand);
	resolveDuplicates(handle);

	ImportDeclarationElementInfo info = new ImportDeclarationElementInfo();
	info.setSourceRangeStart(declarationStart);
	info.setSourceRangeEnd(declarationEnd);
	info.setNameSourceStart(nameSourceStart);
	info.setNameSourceEnd(nameSourceEnd);
	info.setFlags(modifiers);

	addToChildren(this.importContainerInfo, handle);
	this.newElements.put(handle, info);
}
/*
 * Table of line separator position. This table is passed once at the end
 * of the parse action, so as to allow computation of normalized ranges.
 *
 * A line separator might corresponds to several characters in the source,
 *
 */
@Override
public void acceptLineSeparatorPositions(int[] positions) {
	// ignore line separator positions
}
/**
 * @see ISourceElementRequestor
 */
@Override
public void acceptPackage(ImportReference importReference) {

		Object parentInfo = this.infoStack.peek();
		JavaElement parentHandle= (JavaElement) this.handleStack.peek();
		PackageDeclaration handle = null;

		if (parentHandle.getElementType() == IJavaElement.COMPILATION_UNIT) {
			char[] name = CharOperation.concatWith(importReference.getImportName(), '.');
			handle = createPackageDeclaration(parentHandle, new String(name));
		}
		else {
			Assert.isTrue(false); // Should not happen
		}
		resolveDuplicates(handle);

		AnnotatableInfo info = new AnnotatableInfo();
		info.setSourceRangeStart(importReference.declarationSourceStart);
		info.setSourceRangeEnd(importReference.declarationSourceEnd);
		info.setNameSourceStart(importReference.sourceStart);
		info.setNameSourceEnd(importReference.sourceEnd);

		addToChildren(parentInfo, handle);
		this.newElements.put(handle, info);

		if (importReference.annotations != null) {
			for (int i = 0, length = importReference.annotations.length; i < length; i++) {
				org.aspectj.org.eclipse.jdt.internal.compiler.ast.Annotation annotation = importReference.annotations[i];
				acceptAnnotation(annotation, info, handle);
			}
		}
}
@Override
public void acceptProblem(CategorizedProblem problem) {
	if ((problem.getID() & IProblem.Syntax) != 0){
		this.hasSyntaxErrors = true;
	}
}
private void addToChildren(Object parentInfo, JavaElement handle) {
	ArrayList childrenList = (ArrayList) this.children.get(parentInfo);
	if (childrenList == null)
		this.children.put(parentInfo, childrenList = new ArrayList());
	childrenList.add(handle);
}
protected Annotation createAnnotation(JavaElement parent, String name) {
	return new Annotation(parent, name);
}
protected SourceField createField(JavaElement parent, FieldInfo fieldInfo) {
	String fieldName = JavaModelManager.getJavaModelManager().intern(new String(fieldInfo.name));
	return new SourceField(parent, fieldName);
}
protected ImportContainer createImportContainer(ICompilationUnit parent) {
	return (ImportContainer)parent.getImportContainer();
}
protected ImportDeclaration createImportDeclaration(ImportContainer parent, String name, boolean onDemand) {
	return new ImportDeclaration(parent, name, onDemand);
}
protected Initializer createInitializer(JavaElement parent) {
	return new Initializer(parent, 1);
}
protected SourceMethod createMethodHandle(JavaElement parent, MethodInfo methodInfo) {
	String selector = JavaModelManager.getJavaModelManager().intern(new String(methodInfo.name));
	String[] parameterTypeSigs = convertTypeNamesToSigs(methodInfo.parameterTypes);
	return new SourceMethod(parent, selector, parameterTypeSigs);
}
protected PackageDeclaration createPackageDeclaration(JavaElement parent, String name) {
	return new PackageDeclaration((CompilationUnit) parent, name);
}
protected SourceType createTypeHandle(JavaElement parent, TypeInfo typeInfo) {
	String nameString= new String(typeInfo.name);
	return new SourceType(parent, nameString);
}
protected SourceModule createModuleHandle(JavaElement parent, ModuleInfo modInfo) {
	String nameString= new String(modInfo.moduleName);
	return new org.aspectj.org.eclipse.jdt.internal.core.SourceModule(parent, nameString);
}
protected TypeParameter createTypeParameter(JavaElement parent, String name) {
	return new TypeParameter(parent, name);
}
/**
 * Convert these type names to signatures.
 * @see Signature
 */
protected static String[] convertTypeNamesToSigs(char[][] typeNames) {
	if (typeNames == null)
		return CharOperation.NO_STRINGS;
	int n = typeNames.length;
	if (n == 0)
		return CharOperation.NO_STRINGS;
	JavaModelManager manager = JavaModelManager.getJavaModelManager();
	String[] typeSigs = new String[n];
	for (int i = 0; i < n; ++i) {
		typeSigs[i] = manager.intern(Signature.createTypeSignature(typeNames[i], false));
	}
	return typeSigs;
}
protected IAnnotation acceptAnnotation(org.aspectj.org.eclipse.jdt.internal.compiler.ast.Annotation annotation, AnnotatableInfo parentInfo, JavaElement parentHandle) {
	String nameString = new String(CharOperation.concatWith(annotation.type.getTypeName(), '.'));
	Annotation handle = createAnnotation(parentHandle, nameString); //NB: occurenceCount is computed in resolveDuplicates
	resolveDuplicates(handle);

	AnnotationInfo info = new AnnotationInfo();

	// populate the maps here as getValue(...) below may need them
	this.newElements.put(handle, info);
	this.handleStack.push(handle);

	info.setSourceRangeStart(annotation.sourceStart());
	info.nameStart = annotation.type.sourceStart();
	info.nameEnd = annotation.type.sourceEnd();
	MemberValuePair[] memberValuePairs = annotation.memberValuePairs();
	int membersLength = memberValuePairs.length;
	if (membersLength == 0) {
		info.members = Annotation.NO_MEMBER_VALUE_PAIRS;
	} else {
		info.members = getMemberValuePairs(memberValuePairs);
	}

	if (parentInfo != null) {
		IAnnotation[] annotations = parentInfo.annotations;
		int length = annotations.length;
		System.arraycopy(annotations, 0, annotations = new IAnnotation[length+1], 0, length);
		annotations[length] = handle;
		parentInfo.annotations = annotations;
	}
	info.setSourceRangeEnd(annotation.declarationSourceEnd);
	this.handleStack.pop();
	return handle;
}
/**
 * @see ISourceElementRequestor
 */
@Override
public void enterCompilationUnit() {
	this.infoStack = new Stack();
	this.children = new HashMap();
	this.handleStack= new Stack();
	this.infoStack.push(this.unitInfo);
	this.handleStack.push(this.unit);
}
/**
 * @see ISourceElementRequestor
 */
@Override
public void enterConstructor(MethodInfo methodInfo) {
	enterMethod(methodInfo);
}
/**
 * @see ISourceElementRequestor
 */
@Override
public void enterField(FieldInfo fieldInfo) {

	TypeInfo parentInfo = (TypeInfo) this.infoStack.peek();
	JavaElement parentHandle= (JavaElement) this.handleStack.peek();
	SourceField handle = null;
	if (parentHandle.getElementType() == IJavaElement.TYPE) {
		handle = createField(parentHandle, fieldInfo);
	}
	else {
		Assert.isTrue(false); // Should not happen
	}
	resolveDuplicates(handle);

	addToChildren(parentInfo, handle);
	parentInfo.childrenCategories.put(handle, fieldInfo.categories);

	this.infoStack.push(fieldInfo);
	this.handleStack.push(handle);

}
/**
 * @see ISourceElementRequestor
 */
@Override
public void enterInitializer(int declarationSourceStart, int modifiers) {
	Object parentInfo = this.infoStack.peek();
	JavaElement parentHandle= (JavaElement) this.handleStack.peek();
	Initializer handle = null;

	if (parentHandle.getElementType() == IJavaElement.TYPE) {
		handle = createInitializer(parentHandle);
	}
	else {
		Assert.isTrue(false); // Should not happen
	}
	resolveDuplicates(handle);
	
	addToChildren(parentInfo, handle);

	this.infoStack.push(new int[] {declarationSourceStart, modifiers});
	this.handleStack.push(handle);
}
/**
 * @see ISourceElementRequestor
 */
@Override
public void enterMethod(MethodInfo methodInfo) {

	TypeInfo parentInfo = (TypeInfo) this.infoStack.peek();
	JavaElement parentHandle= (JavaElement) this.handleStack.peek();
	SourceMethod handle = null;

	// translate nulls to empty arrays
	if (methodInfo.parameterTypes == null) {
		methodInfo.parameterTypes= CharOperation.NO_CHAR_CHAR;
	}
	if (methodInfo.parameterNames == null) {
		methodInfo.parameterNames= CharOperation.NO_CHAR_CHAR;
	}
	if (methodInfo.exceptionTypes == null) {
		methodInfo.exceptionTypes= CharOperation.NO_CHAR_CHAR;
	}

	if (parentHandle.getElementType() == IJavaElement.TYPE) {
		handle = createMethodHandle(parentHandle, methodInfo);
	}
	else {
		Assert.isTrue(false); // Should not happen
	}
	resolveDuplicates(handle);

	this.infoStack.push(methodInfo);
	this.handleStack.push(handle);
	
	addToChildren(parentInfo, handle);
	parentInfo.childrenCategories.put(handle, methodInfo.categories);
}
private SourceMethodElementInfo createMethodInfo(MethodInfo methodInfo, SourceMethod handle) {
	IJavaElement[] elements = getChildren(methodInfo);
	SourceMethodElementInfo info;
	if (methodInfo.isConstructor) {
		info = elements.length == 0 ? new SourceConstructorInfo() : new SourceConstructorWithChildrenInfo(elements);
	} else if (methodInfo.isAnnotation) {
		info = new SourceAnnotationMethodInfo();
	} else {
		info = elements.length == 0 ? new SourceMethodInfo() : new SourceMethodWithChildrenInfo(elements);
	}
	info.setSourceRangeStart(methodInfo.declarationStart);
	int flags = methodInfo.modifiers;
	info.setNameSourceStart(methodInfo.nameSourceStart);
	info.setNameSourceEnd(methodInfo.nameSourceEnd);
	info.setFlags(flags);
	JavaModelManager manager = JavaModelManager.getJavaModelManager();
	char[][] parameterNames = methodInfo.parameterNames;
	for (int i = 0, length = parameterNames.length; i < length; i++)
		parameterNames[i] = manager.intern(parameterNames[i]);
	info.setArgumentNames(parameterNames);
	char[] returnType = methodInfo.returnType == null ? new char[]{'v', 'o','i', 'd'} : methodInfo.returnType;
	info.setReturnType(manager.intern(returnType));
	char[][] exceptionTypes = methodInfo.exceptionTypes;
	info.setExceptionTypeNames(exceptionTypes);
	for (int i = 0, length = exceptionTypes.length; i < length; i++)
		exceptionTypes[i] = manager.intern(exceptionTypes[i]);
	this.newElements.put(handle, info);

	if (methodInfo.typeParameters != null) {
		for (int i = 0, length = methodInfo.typeParameters.length; i < length; i++) {
			TypeParameterInfo typeParameterInfo = methodInfo.typeParameters[i];
			acceptTypeParameter(typeParameterInfo, info);
		}
	}
	if (methodInfo.annotations != null) {
		int length = methodInfo.annotations.length;
		this.unitInfo.annotationNumber += length;
		for (int i = 0; i < length; i++) {
			org.aspectj.org.eclipse.jdt.internal.compiler.ast.Annotation annotation = methodInfo.annotations[i];
			acceptAnnotation(annotation, info, handle);
		}
	}
	// https://bugs.eclipse.org/bugs/show_bug.cgi?id=334783
	// Process the parameter annotations from the arguments
	if (methodInfo.node != null && methodInfo.node.arguments != null) {
		info.arguments = acceptMethodParameters(methodInfo.node.arguments, handle, methodInfo);
	}
	if (methodInfo.typeAnnotated) {
		this.unitInfo.annotationNumber = CompilationUnitElementInfo.ANNOTATION_THRESHOLD_FOR_DIET_PARSE;
	}
	return info;
}
private LocalVariable[] acceptMethodParameters(Argument[] arguments, JavaElement methodHandle, MethodInfo methodInfo) {
	if (arguments == null) return null;
	LocalVariable[] result = new LocalVariable[arguments.length];
	Annotation[][] paramAnnotations = new Annotation[arguments.length][];
	for(int i = 0; i < arguments.length; i++) {
		Argument argument = arguments[i];
		AnnotatableInfo localVarInfo = new AnnotatableInfo();
		localVarInfo.setSourceRangeStart(argument.declarationSourceStart);
		localVarInfo.setSourceRangeEnd(argument.declarationSourceStart);
		localVarInfo.setNameSourceStart(argument.sourceStart);
		localVarInfo.setNameSourceEnd(argument.sourceEnd);
		
		String paramTypeSig = JavaModelManager.getJavaModelManager().intern(Signature.createTypeSignature(methodInfo.parameterTypes[i], false));
		result[i] = new LocalVariable(
				methodHandle,
				new String(argument.name),
				argument.declarationSourceStart,
				argument.declarationSourceEnd,
				argument.sourceStart,
				argument.sourceEnd,
				paramTypeSig,
				argument.annotations,
				argument.modifiers, 
				true);
		this.newElements.put(result[i], localVarInfo);
		this.infoStack.push(localVarInfo);
		this.handleStack.push(result[i]);
		if (argument.annotations != null) {
			paramAnnotations[i] = new Annotation[argument.annotations.length];
			for (int  j = 0; j < argument.annotations.length; j++ ) {
				org.aspectj.org.eclipse.jdt.internal.compiler.ast.Annotation annotation = argument.annotations[j];
				acceptAnnotation(annotation, localVarInfo, result[i]);
			}
		}
		this.infoStack.pop();
		this.handleStack.pop();
	}
	return result;
}
@Override
public void enterModule(ModuleInfo info) {

	Object parentInfo = this.infoStack.peek();
	JavaElement parentHandle= (JavaElement) this.handleStack.peek();
	JavaElement handle = createModuleHandle(parentHandle, info);
	
	this.infoStack.push(info);
	this.handleStack.push(handle);

	addToChildren(parentInfo, handle);
}
/**
 * @see ISourceElementRequestor
 */
@Override
public void enterType(TypeInfo typeInfo) {

	Object parentInfo = this.infoStack.peek();
	JavaElement parentHandle= (JavaElement) this.handleStack.peek();
	JavaElement handle = createTypeHandle(parentHandle, typeInfo);
	 //NB: occurenceCount is computed in resolveDuplicates
	resolveDuplicates((SourceType) handle);
	this.infoStack.push(typeInfo);
	this.handleStack.push(handle);

	if (parentHandle.getElementType() == IJavaElement.TYPE)
		((TypeInfo) parentInfo).childrenCategories.put(handle, typeInfo.categories);
	addToChildren(parentInfo, handle);
}
private org.aspectj.org.eclipse.jdt.internal.core.ModuleDescriptionInfo createModuleInfo(ModuleInfo modInfo, org.aspectj.org.eclipse.jdt.internal.core.SourceModule handle) {
	org.aspectj.org.eclipse.jdt.internal.core.ModuleDescriptionInfo info = org.aspectj.org.eclipse.jdt.internal.core.ModuleDescriptionInfo.createModule(modInfo.node);
	info.setHandle(handle);
	info.setSourceRangeStart(modInfo.declarationStart);
	info.setFlags(modInfo.modifiers);
	info.setNameSourceStart(modInfo.nameSourceStart);
	info.setNameSourceEnd(modInfo.nameSourceEnd);
	info.addCategories(handle, modInfo.categories);
	if (modInfo.annotations != null) {
		int length = modInfo.annotations.length;
		for (int i = 0; i < length; i++) {
			org.aspectj.org.eclipse.jdt.internal.compiler.ast.Annotation annotation = modInfo.annotations[i];
			acceptAnnotation(annotation, info, handle);
		}
	}
	this.newElements.put(handle, info);

	return info;
}
private SourceTypeElementInfo createTypeInfo(TypeInfo typeInfo, SourceType handle) {
	SourceTypeElementInfo info =
		typeInfo.anonymousMember ?
			new SourceTypeElementInfo() {
				@Override
				public boolean isAnonymousMember() {
					return true;
				}
			} :
		new SourceTypeElementInfo();
	info.setHandle(handle);
	info.setSourceRangeStart(typeInfo.declarationStart);
	info.setFlags(typeInfo.modifiers);
	info.setNameSourceStart(typeInfo.nameSourceStart);
	info.setNameSourceEnd(typeInfo.nameSourceEnd);
	JavaModelManager manager = JavaModelManager.getJavaModelManager();
	char[] superclass = typeInfo.superclass;
	info.setSuperclassName(superclass == null ? null : manager.intern(superclass));
	char[][] superinterfaces = typeInfo.superinterfaces;
	for (int i = 0, length = superinterfaces == null ? 0 : superinterfaces.length; i < length; i++)
		superinterfaces[i] = manager.intern(superinterfaces[i]);
	info.setSuperInterfaceNames(superinterfaces);
	info.addCategories(handle, typeInfo.categories);
	this.newElements.put(handle, info);

	if (typeInfo.typeParameters != null) {
		for (int i = 0, length = typeInfo.typeParameters.length; i < length; i++) {
			TypeParameterInfo typeParameterInfo = typeInfo.typeParameters[i];
			acceptTypeParameter(typeParameterInfo, info);
		}
	}
	if (typeInfo.annotations != null) {
		int length = typeInfo.annotations.length;
		this.unitInfo.annotationNumber += length;
		for (int i = 0; i < length; i++) {
			org.aspectj.org.eclipse.jdt.internal.compiler.ast.Annotation annotation = typeInfo.annotations[i];
			acceptAnnotation(annotation, info, handle);
		}
	}
	if (typeInfo.childrenCategories != null) {
		Iterator iterator = typeInfo.childrenCategories.entrySet().iterator();
		while (iterator.hasNext()) {
			Map.Entry entry = (Map.Entry) iterator.next();
			info.addCategories((IJavaElement) entry.getKey(), (char[][]) entry.getValue());
		}
		
	}
	if (typeInfo.typeAnnotated) {
		this.unitInfo.annotationNumber = CompilationUnitElementInfo.ANNOTATION_THRESHOLD_FOR_DIET_PARSE;
	}
	return info;
}
protected void acceptTypeParameter(TypeParameterInfo typeParameterInfo, JavaElementInfo parentInfo) {
	JavaElement parentHandle = (JavaElement) this.handleStack.peek();
	String nameString = new String(typeParameterInfo.name);
	TypeParameter handle = createTypeParameter(parentHandle, nameString); //NB: occurenceCount is computed in resolveDuplicates
	resolveDuplicates(handle);

	TypeParameterElementInfo info = new TypeParameterElementInfo();
	info.setSourceRangeStart(typeParameterInfo.declarationStart);
	info.nameStart = typeParameterInfo.nameSourceStart;
	info.nameEnd = typeParameterInfo.nameSourceEnd;
	info.bounds = typeParameterInfo.bounds;
	if (parentInfo instanceof SourceTypeElementInfo) {
		SourceTypeElementInfo elementInfo = (SourceTypeElementInfo) parentInfo;
		ITypeParameter[] typeParameters = elementInfo.typeParameters;
		int length = typeParameters.length;
		System.arraycopy(typeParameters, 0, typeParameters = new ITypeParameter[length+1], 0, length);
		typeParameters[length] = handle;
		elementInfo.typeParameters = typeParameters;
	} else {
		SourceMethodElementInfo elementInfo = (SourceMethodElementInfo) parentInfo;
		ITypeParameter[] typeParameters = elementInfo.typeParameters;
		int length = typeParameters.length;
		System.arraycopy(typeParameters, 0, typeParameters = new ITypeParameter[length+1], 0, length);
		typeParameters[length] = handle;
		elementInfo.typeParameters = typeParameters;
	}
	this.newElements.put(handle, info);
	info.setSourceRangeEnd(typeParameterInfo.declarationEnd);
	if (typeParameterInfo.typeAnnotated) {
		this.unitInfo.annotationNumber = CompilationUnitElementInfo.ANNOTATION_THRESHOLD_FOR_DIET_PARSE;
	}
}
/**
 * @see ISourceElementRequestor
 */
@Override
public void exitCompilationUnit(int declarationEnd) {
	// set import container children
	if (this.importContainerInfo != null) {
		this.importContainerInfo.children = getChildren(this.importContainerInfo);
	}

	this.unitInfo.children = getChildren(this.unitInfo);
	this.unitInfo.setSourceLength(declarationEnd + 1);

	// determine if there were any parsing errors
	this.unitInfo.setIsStructureKnown(!this.hasSyntaxErrors);
}
/**
 * @see ISourceElementRequestor
 */
@Override
public void exitConstructor(int declarationEnd) {
	exitMethod(declarationEnd, null);
}
/**
 * @see ISourceElementRequestor
 */
@Override
public void exitField(int initializationStart, int declarationEnd, int declarationSourceEnd) {
	JavaElement handle = (JavaElement) this.handleStack.peek();
	FieldInfo fieldInfo = (FieldInfo) this.infoStack.peek();
	IJavaElement[] elements = getChildren(fieldInfo);
	SourceFieldElementInfo info = elements.length == 0 ? new SourceFieldElementInfo() : new SourceFieldWithChildrenInfo(elements);
	info.setNameSourceStart(fieldInfo.nameSourceStart);
	info.setNameSourceEnd(fieldInfo.nameSourceEnd);
	info.setSourceRangeStart(fieldInfo.declarationStart);
	info.setFlags(fieldInfo.modifiers);
	char[] typeName = JavaModelManager.getJavaModelManager().intern(fieldInfo.type);
	info.setTypeName(typeName);
	this.newElements.put(handle, info);

	if (fieldInfo.annotations != null) {
		int length = fieldInfo.annotations.length;
		this.unitInfo.annotationNumber += length;
		for (int i = 0; i < length; i++) {
			org.aspectj.org.eclipse.jdt.internal.compiler.ast.Annotation annotation = fieldInfo.annotations[i];
			acceptAnnotation(annotation, info, handle);
		}
	}
	info.setSourceRangeEnd(declarationSourceEnd);
	this.handleStack.pop();
	this.infoStack.pop();
	
	// remember initializer source if field is a constant
	if (initializationStart != -1) {
		int flags = info.flags;
		Object typeInfo;
		if (Flags.isFinal(flags)
				|| ((typeInfo = this.infoStack.peek()) instanceof TypeInfo
					 && (Flags.isInterface(((TypeInfo)typeInfo).modifiers)))) {
			int length = declarationEnd - initializationStart;
			if (length > 0) {
				char[] initializer = new char[length];
				System.arraycopy(this.parser.scanner.source, initializationStart, initializer, 0, length);
				info.initializationSource = initializer;
			}
		}
	}
	if (fieldInfo.typeAnnotated) {
		this.unitInfo.annotationNumber = CompilationUnitElementInfo.ANNOTATION_THRESHOLD_FOR_DIET_PARSE;
	}
}
/**
 * @see ISourceElementRequestor
 */
@Override
public void exitInitializer(int declarationEnd) {
	JavaElement handle = (JavaElement) this.handleStack.peek();
	int[] initializerInfo = (int[]) this.infoStack.peek();
	IJavaElement[] elements = getChildren(initializerInfo);
	
	InitializerElementInfo info = elements.length == 0 ? new InitializerElementInfo() : new InitializerWithChildrenInfo(elements);
	info.setSourceRangeStart(initializerInfo[0]);
	info.setFlags(initializerInfo[1]);
	info.setSourceRangeEnd(declarationEnd);

	this.newElements.put(handle, info);
	
	this.handleStack.pop();
	this.infoStack.pop();
}
/**
 * @see ISourceElementRequestor
 */
@Override
public void exitMethod(int declarationEnd, Expression defaultValue) {
	SourceMethod handle = (SourceMethod) this.handleStack.peek();
	MethodInfo methodInfo = (MethodInfo) this.infoStack.peek();
	
	SourceMethodElementInfo info = createMethodInfo(methodInfo, handle);
	info.setSourceRangeEnd(declarationEnd);
	
	// remember default value of annotation method
	if (info.isAnnotationMethod() && defaultValue != null) {
		SourceAnnotationMethodInfo annotationMethodInfo = (SourceAnnotationMethodInfo) info;
		annotationMethodInfo.defaultValueStart = defaultValue.sourceStart;
		annotationMethodInfo.defaultValueEnd = defaultValue.sourceEnd;
		JavaElement element = (JavaElement) this.handleStack.peek();
		org.aspectj.org.eclipse.jdt.internal.core.MemberValuePair defaultMemberValuePair = new org.aspectj.org.eclipse.jdt.internal.core.MemberValuePair(element.getElementName());
		defaultMemberValuePair.value = getMemberValue(defaultMemberValuePair, defaultValue);
		annotationMethodInfo.defaultValue = defaultMemberValuePair;
	}
	
	this.handleStack.pop();
	this.infoStack.pop();
}
@Override
public void exitModule(int declarationEnd) {
	ModuleInfo moduleInfo = (ModuleInfo) this.infoStack.peek();
	SourceModule handle = (SourceModule) this.handleStack.peek();
	JavaProject proj = (JavaProject) handle.getAncestor(IJavaElement.JAVA_PROJECT);
	if (proj != null) {
		try {
			org.aspectj.org.eclipse.jdt.internal.core.SourceModule moduleDecl = handle;
			org.aspectj.org.eclipse.jdt.internal.core.ModuleDescriptionInfo info = createModuleInfo(moduleInfo, moduleDecl);
			info.setSourceRangeEnd(declarationEnd);
			info.children = getChildren(info);
			this.unitInfo.setModule(moduleDecl);
			proj.setModuleDescription(moduleDecl);
		} catch (JavaModelException e) {
			// Unexpected while creating
		}
	}
	this.handleStack.pop();
	this.infoStack.pop();
}
/**
 * @see ISourceElementRequestor
 */
@Override
public void exitType(int declarationEnd) {
	TypeInfo typeInfo = (TypeInfo) this.infoStack.peek();
	SourceType handle = (SourceType) this.handleStack.peek();
	SourceTypeElementInfo info = createTypeInfo(typeInfo, handle);
	info.setSourceRangeEnd(declarationEnd);
	info.children = getChildren(typeInfo);
	this.handleStack.pop();
	this.infoStack.pop();
}
/**
 * Resolves duplicate handles by incrementing the occurrence count
 * of the handle being created.
 */
protected void resolveDuplicates(SourceRefElement handle) {
	int occurenceCount = this.occurenceCounts.get(handle);
	if (occurenceCount == -1)
		this.occurenceCounts.put(handle, 1);
	else {
		this.occurenceCounts.put(handle, ++occurenceCount);
		handle.occurrenceCount = occurenceCount;
	}

	// https://bugs.eclipse.org/bugs/show_bug.cgi?id=342393
	// For anonymous source types, the occurrence count should be in the context
	// of the enclosing type.
	if (handle instanceof SourceType && ((SourceType) handle).isAnonymous()) {
		Object key = handle.getParent().getAncestor(IJavaElement.TYPE);
		occurenceCount = this.localOccurrenceCounts.get(key);
		if (occurenceCount == -1)
			this.localOccurrenceCounts.put(key, 1);
		else {
			this.localOccurrenceCounts.put(key, ++occurenceCount);
			((SourceType)handle).localOccurrenceCount = occurenceCount;
		}
	}
}
protected IMemberValuePair getMemberValuePair(MemberValuePair memberValuePair) {
	String memberName = new String(memberValuePair.name);
	org.aspectj.org.eclipse.jdt.internal.core.MemberValuePair result = new org.aspectj.org.eclipse.jdt.internal.core.MemberValuePair(memberName);
	result.value = getMemberValue(result, memberValuePair.value);
	return result;
}
protected IMemberValuePair[] getMemberValuePairs(MemberValuePair[] memberValuePairs) {
	int membersLength = memberValuePairs.length;
	IMemberValuePair[] members = new IMemberValuePair[membersLength];
	for (int j = 0; j < membersLength; j++) {
		members[j] = getMemberValuePair(memberValuePairs[j]);
	}
	return members;
}
private IJavaElement[] getChildren(Object info) {
	ArrayList childrenList = (ArrayList) this.children.get(info);
	if (childrenList != null) {
		return (IJavaElement[]) childrenList.toArray(new IJavaElement[childrenList.size()]);
	}
	return JavaElement.NO_ELEMENTS;
}
/*
 * Creates the value from the given expression, and sets the valueKind on the given memberValuePair
 */
protected Object getMemberValue(org.aspectj.org.eclipse.jdt.internal.core.MemberValuePair memberValuePair, Expression expression) {
	if (expression instanceof NullLiteral) {
		return null;
	} else if (expression instanceof Literal) {
		((Literal) expression).computeConstant();
		return Util.getAnnotationMemberValue(memberValuePair, expression.constant);
	} else if (expression instanceof org.aspectj.org.eclipse.jdt.internal.compiler.ast.Annotation) {
		org.aspectj.org.eclipse.jdt.internal.compiler.ast.Annotation annotation = (org.aspectj.org.eclipse.jdt.internal.compiler.ast.Annotation) expression;
		Object handle = acceptAnnotation(annotation, null, (JavaElement) this.handleStack.peek());
		memberValuePair.valueKind = IMemberValuePair.K_ANNOTATION;
		return handle;
	} else if (expression instanceof ClassLiteralAccess) {
		ClassLiteralAccess classLiteral = (ClassLiteralAccess) expression;
		char[] name = CharOperation.concatWith(classLiteral.type.getTypeName(), '.');
		memberValuePair.valueKind = IMemberValuePair.K_CLASS;
		return new String(name);
	} else if (expression instanceof QualifiedNameReference) {
		char[] qualifiedName = CharOperation.concatWith(((QualifiedNameReference) expression).tokens, '.');
		memberValuePair.valueKind = IMemberValuePair.K_QUALIFIED_NAME;
		return new String(qualifiedName);
	} else if (expression instanceof SingleNameReference) {
		char[] simpleName = ((SingleNameReference) expression).token;
		if (simpleName == RecoveryScanner.FAKE_IDENTIFIER) {
			memberValuePair.valueKind = IMemberValuePair.K_UNKNOWN;
			return null;
		}
		memberValuePair.valueKind = IMemberValuePair.K_SIMPLE_NAME;
		return new String(simpleName);
	} else if (expression instanceof ArrayInitializer) {
		memberValuePair.valueKind = -1; // modified below by the first call to getMemberValue(...)
		Expression[] expressions = ((ArrayInitializer) expression).expressions;
		int length = expressions == null ? 0 : expressions.length;
		Object[] values = new Object[length];
		for (int i = 0; i < length; i++) {
			int previousValueKind = memberValuePair.valueKind;
			Object value = getMemberValue(memberValuePair, expressions[i]);
			if (previousValueKind != -1 && memberValuePair.valueKind != previousValueKind) {
				// values are heterogeneous, value kind is thus unknown
				memberValuePair.valueKind = IMemberValuePair.K_UNKNOWN;
			}
			values[i] = value;
		}
		if (memberValuePair.valueKind == -1)
			memberValuePair.valueKind = IMemberValuePair.K_UNKNOWN;
		return values;
	} else if (expression instanceof UnaryExpression) {			// to deal with negative numerals (see bug - 248312)
		UnaryExpression unaryExpression = (UnaryExpression) expression;
		if ((unaryExpression.bits & ASTNode.OperatorMASK) >> ASTNode.OperatorSHIFT == OperatorIds.MINUS) {
			if (unaryExpression.expression instanceof Literal) {
				Literal subExpression = (Literal) unaryExpression.expression;
				subExpression.computeConstant();
				return Util.getNegativeAnnotationMemberValue(memberValuePair, subExpression.constant);
			}
		}
		memberValuePair.valueKind = IMemberValuePair.K_UNKNOWN;
		return null;
	} else {
		memberValuePair.valueKind = IMemberValuePair.K_UNKNOWN;
		return null;
	}
}
}
