/* *******************************************************************
 * 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	revisions, added additional relationships
 *     Alexandre Vasseur        support for @AJ style
 * ******************************************************************/

package org.aspectj.ajdt.internal.core.builder;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Stack;
import java.util.StringTokenizer;

import org.aspectj.ajdt.internal.compiler.CompilationResultDestinationManager;
import org.aspectj.ajdt.internal.compiler.ast.AdviceDeclaration;
import org.aspectj.ajdt.internal.compiler.ast.AspectDeclaration;
import org.aspectj.ajdt.internal.compiler.ast.DeclareDeclaration;
import org.aspectj.ajdt.internal.compiler.ast.InterTypeDeclaration;
import org.aspectj.ajdt.internal.compiler.ast.InterTypeFieldDeclaration;
import org.aspectj.ajdt.internal.compiler.ast.PointcutDeclaration;
import org.aspectj.ajdt.internal.compiler.lookup.AjLookupEnvironment;
import org.aspectj.ajdt.internal.compiler.lookup.EclipseFactory;
import org.aspectj.asm.AsmManager;
import org.aspectj.asm.IProgramElement;
import org.aspectj.asm.internal.CharOperation;
import org.aspectj.asm.internal.ProgramElement;
import org.aspectj.bridge.ISourceLocation;
import org.aspectj.bridge.SourceLocation;
import org.aspectj.org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.aspectj.org.eclipse.jdt.internal.compiler.CompilationResult;
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.AbstractVariableDeclaration;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.ImportReference;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.Initializer;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.Literal;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.OperatorExpression;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.Reference;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.aspectj.org.eclipse.jdt.internal.compiler.ast.TypeParameter;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.ClassScope;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
import org.aspectj.org.eclipse.jdt.internal.compiler.lookup.MethodScope;
import org.aspectj.org.eclipse.jdt.internal.compiler.util.Util;
import org.aspectj.util.LangUtil;
import org.aspectj.weaver.BCException;
import org.aspectj.weaver.Member;
import org.aspectj.weaver.ResolvedMember;
import org.aspectj.weaver.ResolvedType;
import org.aspectj.weaver.UnresolvedType;
import org.aspectj.weaver.World;
import org.aspectj.weaver.patterns.AndPointcut;
import org.aspectj.weaver.patterns.Declare;
import org.aspectj.weaver.patterns.DeclareAnnotation;
import org.aspectj.weaver.patterns.DeclareParents;
import org.aspectj.weaver.patterns.OrPointcut;
import org.aspectj.weaver.patterns.Pointcut;
import org.aspectj.weaver.patterns.ReferencePointcut;
import org.aspectj.weaver.patterns.TypePatternList;

/**
 * At each iteration of <CODE>processCompilationUnit</CODE> the declarations for a particular compilation unit are added to the
 * hierarchy passed as a a parameter.
 * <p>
 * Clients who extend this class need to ensure that they do not override any of the existing behavior. If they do, the structure
 * model will not be built properly and tools such as IDE structure views and ajdoc will fail.
 * <p>
 * <b>Note:</b> this class is not considered public API and the overridable methods are subject to change.
 * 
 * @author Mik Kersten
 */
public class AsmHierarchyBuilder extends ASTVisitor {

	protected AsmElementFormatter formatter = new AsmElementFormatter();
	// pr148027 - stop generating uses pointcut/pointcut used by relationship
	// until we do it in the same way as other relationships.
	// public static boolean shouldAddUsesPointcut = false;
	/**
	 * Reset for every compilation unit.
	 */
	protected AjBuildConfig buildConfig;

	/**
	 * Reset for every compilation unit.
	 */
	protected Stack<IProgramElement> stack;

	protected ImportReference packageDecl = null;

	/**
	 * Reset for every compilation unit.
	 */
	private CompilationResult currCompilationResult;
	private String filename;
	int[] lineseps;

	/**
	 * 
	 * @param cuDeclaration
	 * @param buildConfig
	 * @param structureModel
	 *            hiearchy to add this unit's declarations to
	 */
	public void buildStructureForCompilationUnit(CompilationUnitDeclaration cuDeclaration, AsmManager structureModel,
			AjBuildConfig buildConfig) {
		currCompilationResult = cuDeclaration.compilationResult();
		filename = new String(currCompilationResult.fileName);
		lineseps = currCompilationResult.lineSeparatorPositions;
		LangUtil.throwIaxIfNull(currCompilationResult, "result");
		stack = new Stack();
		packageDecl = null;
		this.buildConfig = buildConfig;
		internalBuild(cuDeclaration, structureModel);
		this.buildConfig = null; // clear reference since this structure is
		// anchored in static
		currCompilationResult = null;
		stack.clear();
		// throw new RuntimeException("not implemented");
	}

	private void internalBuild(CompilationUnitDeclaration unit, AsmManager structureModel) {
		LangUtil.throwIaxIfNull(structureModel, "structureModel");
		try {
			activeStructureModel = structureModel;
			// if (!currCompilationResult.equals(unit.compilationResult())) {
			// throw new IllegalArgumentException("invalid unit: " + unit);
			// }
			// ---- summary
			// add unit to package (or root if no package),
			// first removing any duplicate (XXX? removes children if 3 classes in
			// same file?)
			// push the node on the stack
			// and traverse

			// -- create node to add
			final File file = new File(new String(unit.getFileName()));
			final IProgramElement cuNode;
			{
				// AMC - use the source start and end from the compilation unit decl
				int startLine = getStartLine(unit);
				int endLine = getEndLine(unit);
				SourceLocation sourceLocation = new SourceLocation(file, startLine, endLine);
				sourceLocation.setOffset(unit.sourceStart);
				cuNode = new ProgramElement(structureModel, new String(file.getName()), IProgramElement.Kind.FILE_JAVA,
						sourceLocation, 0, null, null);
			}

			// container for import declarations - this may move to position 1 in the child list, if there
			// is a package declaration
			cuNode.addChild(new ProgramElement(structureModel, "", IProgramElement.Kind.IMPORT_REFERENCE, null, 0, null, null));

			final IProgramElement addToNode = genAddToNode(file, unit, structureModel);

			// -- remove duplicates before adding (XXX use them instead?)
			if (addToNode != null && addToNode.getChildren() != null) {
				for (ListIterator itt = addToNode.getChildren().listIterator(); itt.hasNext();) {
					IProgramElement child = (IProgramElement) itt.next();
					ISourceLocation childLoc = child.getSourceLocation();
					if (null == childLoc) {
						// XXX ok, packages have null source locations
						// signal others?
					} else if (childLoc.getSourceFile().equals(file)) {
						itt.remove();
					}
				}
			}
			// -- add and traverse
			addToNode.addChild(cuNode);
			stack.push(cuNode);
			unit.traverse(this, unit.scope);

			// -- update file map (XXX do this before traversal?)
			try {
				structureModel.getHierarchy().addToFileMap(file.getCanonicalPath(), cuNode);
			} catch (IOException e) {
				System.err.println("IOException " + e.getMessage() + " creating path for " + file);
				// XXX signal IOException when canonicalizing file path
			}
		} finally {
			activeStructureModel = null;
		}

	}

	private AsmManager activeStructureModel = null;

	private IProgramElement findOrCreateChildSourceFolder(String sourceFolder, AsmManager structureModel) {
		IProgramElement root = structureModel.getHierarchy().getRoot();
		// Check if already there
		IProgramElement sourceFolderNode = null;
		List<IProgramElement> kids = root.getChildren();
		for (Iterator<IProgramElement> iterator = kids.iterator(); iterator.hasNext();) {
			IProgramElement child = (IProgramElement) iterator.next();
			if (child.getKind() == IProgramElement.Kind.SOURCE_FOLDER && child.getName().equals(sourceFolder)) {
				sourceFolderNode = child;
				break;
			}
		}
		if (sourceFolderNode == null) {
			sourceFolderNode = new ProgramElement(structureModel, sourceFolder, IProgramElement.Kind.SOURCE_FOLDER, new ArrayList());
			root.addChild(sourceFolderNode);
		}
		return sourceFolderNode;
	}

	/**
	 * Get/create the node (package or root) to add to.
	 */
	private IProgramElement genAddToNode(File sourceFile, CompilationUnitDeclaration unit, AsmManager structureModel) {
		final IProgramElement addToNode;
		{

			CompilationResultDestinationManager manager = buildConfig.getCompilationResultDestinationManager();
			String sourceFolder = (manager == null ? null : manager.getSourceFolderForFile(sourceFile));

			ImportReference unitPackage = unit.currentPackage;

			// if (null == unitPackage) {
			// // Is there a sourceFolder to stick in?
			// if (sourceFolder == null) {
			// addToNode = structureModel.getRoot();
			// } else {
			// addToNode = findOrCreateChildSourceFolder(sourceFolder, structureModel);
			// }
			// } else {

			IProgramElement rootForSource = structureModel.getHierarchy().getRoot();
			if (sourceFolder != null) {
				rootForSource = findOrCreateChildSourceFolder(sourceFolder, structureModel);
			}
			String pkgName;
			if (unitPackage == null) {
				pkgName = "";
			} else {
				StringBuffer nameBuffer = new StringBuffer();
				final char[][] importName = unitPackage.getImportName();
				final int last = importName.length - 1;
				for (int i = 0; i < importName.length; i++) {
					nameBuffer.append(new String(importName[i]));
					if (i < last) {
						nameBuffer.append('.');
					}
				}
				pkgName = nameBuffer.toString();
			}

			IProgramElement pkgNode = null;
			if (structureModel != null && structureModel.getHierarchy().getRoot() != null && rootForSource.getChildren() != null) {
				for (Iterator<IProgramElement> it = rootForSource.getChildren().iterator(); it.hasNext();) {
					IProgramElement currNode = (IProgramElement) it.next();
					if (pkgName.equals(currNode.getName())) {
						pkgNode = currNode;
						break;
					}
				}
			}
			if (pkgNode == null) {
				// note packages themselves have no source location
				pkgNode = new ProgramElement(activeStructureModel, pkgName, IProgramElement.Kind.PACKAGE, new ArrayList());
				rootForSource.addChild(pkgNode);
			}
			addToNode = pkgNode;
			// }
		}
		return addToNode;
	}

	public boolean visit(TypeDeclaration typeDeclaration, CompilationUnitScope scope) {
		String name = new String(typeDeclaration.name);
		IProgramElement.Kind kind = IProgramElement.Kind.CLASS;
		if (typeDeclaration instanceof AspectDeclaration) {
			kind = IProgramElement.Kind.ASPECT;
		} else if (TypeDeclaration.kind(typeDeclaration.modifiers) == TypeDeclaration.INTERFACE_DECL) {
			kind = IProgramElement.Kind.INTERFACE;
		} else if (TypeDeclaration.kind(typeDeclaration.modifiers) == TypeDeclaration.ENUM_DECL) {
			kind = IProgramElement.Kind.ENUM;
		} else if (TypeDeclaration.kind(typeDeclaration.modifiers) == TypeDeclaration.ANNOTATION_TYPE_DECL) {
			kind = IProgramElement.Kind.ANNOTATION;
		}

		boolean isAnnotationStyleAspect = false;
		// @AJ support
		if (typeDeclaration.annotations != null) {
			for (int i = 0; i < typeDeclaration.annotations.length; i++) {
				Annotation annotation = typeDeclaration.annotations[i];
				if (Arrays.equals(annotation.type.getTypeBindingPublic(scope).signature(),
						"Lorg/aspectj/lang/annotation/Aspect;".toCharArray())) {
					kind = IProgramElement.Kind.ASPECT;
					if (!(typeDeclaration instanceof AspectDeclaration)) {
						isAnnotationStyleAspect = true;
					}
				} else if (annotation.resolvedType != null) {
					// Fix for the case where in a privileged aspect a parent declaration :
					// declare parents: (@A C+) implements (B);
					// is causing the resolvedType to be null when it shouldn't
					// for the aspect and privileged annotation of that aspect.

					// Creating the char[][] needed for ImportReference
					String[] temp = (new String(annotation.resolvedType.constantPoolName())).split("/");
					if (temp.length > 1) {
						char[][] path = new char[temp.length][];
						for (int k = 0; k < temp.length; k++) {
							path[k] = temp[k].toCharArray();
						}

						// Create the ImportReference needed to add a
						// ProgramElement
						ImportReference importRef = new ImportReference(path, new long[] { 0 }, false, 0);
						IProgramElement ceNode = new ProgramElement(activeStructureModel, importRef.toString(),
								IProgramElement.Kind.IMPORT_REFERENCE, makeLocation(importRef), 0, null, null);
						ceNode.setSourceSignature(genSourceSignature(importRef));
						// Add Element to Imports of Current Class
						ProgramElement imports = getImportReferencesRoot();// (ProgramElement) ((IProgramElement)
						// stack.peek()).getChildren().get(0);
						imports.addChild(0, ceNode);
					}
				}
			}
		}

		int typeModifiers = typeDeclaration.modifiers;
		if (typeDeclaration instanceof AspectDeclaration) {
			typeModifiers = ((AspectDeclaration) typeDeclaration).getDeclaredModifiers();
		}

		IProgramElement peNode = new ProgramElement(activeStructureModel, name, kind, makeLocation(typeDeclaration), typeModifiers,
				null, null);
		peNode.setSourceSignature(genSourceSignature(typeDeclaration));
		peNode.setFormalComment(generateJavadocComment(typeDeclaration));
		peNode.setAnnotationStyleDeclaration(isAnnotationStyleAspect);

		stack.peek().addChild(peNode);
		stack.push(peNode);
		return true;
	}

	public void endVisit(TypeDeclaration typeDeclaration, CompilationUnitScope scope) {
		// Is there a package declaration to insert into the model?
		if (packageDecl != null) {
			int dotIndex = packageDecl.toString().lastIndexOf('.');
			String packageString = packageDecl.toString();
			ProgramElement packageDeclaration = new ProgramElement(activeStructureModel, packageString,
					IProgramElement.Kind.PACKAGE_DECLARATION, makeLocation(packageDecl), 0, null, null);
			StringBuffer packageSourceDeclaration = new StringBuffer();
			packageSourceDeclaration.append("package ");
			packageSourceDeclaration.append(packageString);
			packageSourceDeclaration.append(";");
			packageDeclaration.setSourceSignature(packageSourceDeclaration.toString());
			stack.pop();
			ProgramElement containingTypeElement = (ProgramElement) stack.peek();
			containingTypeElement.addChild(0, packageDeclaration);
			packageDecl = null;
		} else {
			stack.pop();
		}
	}

	// ??? share impl with visit(TypeDeclaration, ..) ?
	public boolean visit(TypeDeclaration memberTypeDeclaration, ClassScope scope) {
		String name = new String(memberTypeDeclaration.name);

		IProgramElement.Kind kind = IProgramElement.Kind.CLASS;
		int typeDeclarationKind = TypeDeclaration.kind(memberTypeDeclaration.modifiers);
		if (memberTypeDeclaration instanceof AspectDeclaration) {
			kind = IProgramElement.Kind.ASPECT;
		} else if (typeDeclarationKind == TypeDeclaration.INTERFACE_DECL) {
			kind = IProgramElement.Kind.INTERFACE;
		} else if (typeDeclarationKind == TypeDeclaration.ENUM_DECL) {
			kind = IProgramElement.Kind.ENUM;
		} else if (typeDeclarationKind == TypeDeclaration.ANNOTATION_TYPE_DECL) {
			kind = IProgramElement.Kind.ANNOTATION;
		}

		boolean isAnnotationStyleAspect = false;
		// @AJ support
		if (memberTypeDeclaration.annotations != null) {
			for (int i = 0; i < memberTypeDeclaration.annotations.length; i++) {
				Annotation annotation = memberTypeDeclaration.annotations[i];
				if (Arrays.equals(annotation.type.getTypeBindingPublic(scope).signature(),
						"Lorg/aspectj/lang/annotation/Aspect;".toCharArray())) {
					kind = IProgramElement.Kind.ASPECT;
					if (!(memberTypeDeclaration instanceof AspectDeclaration)) {
						isAnnotationStyleAspect = true;
					}
				}
			}
		}

		int typeModifiers = memberTypeDeclaration.modifiers;
		if (memberTypeDeclaration instanceof AspectDeclaration) {
			typeModifiers = ((AspectDeclaration) memberTypeDeclaration).getDeclaredModifiers();
		}

		IProgramElement peNode = new ProgramElement(activeStructureModel, name, kind, makeLocation(memberTypeDeclaration),
				typeModifiers, null, null);
		peNode.setSourceSignature(genSourceSignature(memberTypeDeclaration));
		peNode.setFormalComment(generateJavadocComment(memberTypeDeclaration));
		peNode.setAnnotationStyleDeclaration(isAnnotationStyleAspect);

		((IProgramElement) stack.peek()).addChild(peNode);
		stack.push(peNode);
		return true;
	}

	public void endVisit(TypeDeclaration memberTypeDeclaration, ClassScope scope) {
		stack.pop();
	}

	public boolean visit(TypeDeclaration memberTypeDeclaration, BlockScope scope) {
		String fullName = "<undefined>";
		if (memberTypeDeclaration.allocation != null && memberTypeDeclaration.allocation.type != null) {
			// Create a name something like 'new Runnable() {..}'
			fullName = "new " + memberTypeDeclaration.allocation.type.toString() + "() {..}";
		} else if (memberTypeDeclaration.binding != null && memberTypeDeclaration.binding.constantPoolName() != null) {
			// If we couldn't find a nice name like 'new Runnable() {..}' then
			// use the number after the $
			fullName = new String(memberTypeDeclaration.name);
			// fullName = new String(memberTypeDeclaration.binding
			// .constantPoolName());

			int dollar = fullName.indexOf('$');
			fullName = fullName.substring(dollar + 1);
		}

		IProgramElement.Kind kind = IProgramElement.Kind.CLASS;
		if (TypeDeclaration.kind(memberTypeDeclaration.modifiers) == TypeDeclaration.INTERFACE_DECL) {
			kind = IProgramElement.Kind.INTERFACE;
		} else if (TypeDeclaration.kind(memberTypeDeclaration.modifiers) == TypeDeclaration.ENUM_DECL) {
			kind = IProgramElement.Kind.ENUM;
		} else if (TypeDeclaration.kind(memberTypeDeclaration.modifiers) == TypeDeclaration.ANNOTATION_TYPE_DECL) {
			kind = IProgramElement.Kind.ANNOTATION;
		}

		// @AJ support
		boolean isAnnotationStyleAspect = false;
		if (memberTypeDeclaration.annotations != null) {
			for (int i = 0; i < memberTypeDeclaration.annotations.length; i++) {
				Annotation annotation = memberTypeDeclaration.annotations[i];
				if (Arrays.equals(annotation.type.getTypeBindingPublic(scope).signature(),
						"Lorg/aspectj/lang/annotation/Aspect;".toCharArray())) {
					kind = IProgramElement.Kind.ASPECT;
					if (!(memberTypeDeclaration instanceof AspectDeclaration)) {
						isAnnotationStyleAspect = true;
					}
					break;
				}
			}
		}

		IProgramElement peNode = new ProgramElement(activeStructureModel, fullName, kind, makeLocation(memberTypeDeclaration),
				memberTypeDeclaration.modifiers, null, null);
		peNode.setSourceSignature(genSourceSignature(memberTypeDeclaration));
		peNode.setFormalComment(generateJavadocComment(memberTypeDeclaration));
		peNode.setAnnotationStyleDeclaration(isAnnotationStyleAspect);
		// if we're something like 'new Runnable(){..}' then set the
		// bytecodeSignature to be the typename so we can match it later
		// when creating the structure model
		if (peNode.getBytecodeSignature() == null && memberTypeDeclaration.binding != null
				&& memberTypeDeclaration.binding.constantPoolName() != null) {
			StringTokenizer st = new StringTokenizer(new String(memberTypeDeclaration.binding.constantPoolName()), "/");
			while (st.hasMoreTokens()) {
				String s = st.nextToken();
				if (!st.hasMoreTokens()) {
					peNode.setBytecodeSignature(s);
				}
			}
		}

		((IProgramElement) stack.peek()).addChild(peNode);
		stack.push(peNode);
		return true;
	}

	public void endVisit(TypeDeclaration memberTypeDeclaration, BlockScope scope) {
		stack.pop();
	}

	private String genSourceSignature(TypeDeclaration typeDeclaration) {
		StringBuffer output = new StringBuffer();
		typeDeclaration.printHeader(0, output);
		return output.toString();
	}

	// private IProgramElement findEnclosingClass(Stack stack) {
	// for (int i = stack.size() - 1; i >= 0; i--) {
	// IProgramElement pe = (IProgramElement) stack.get(i);
	// if (pe.getKind() == IProgramElement.Kind.CLASS) {
	// return pe;
	// }
	//
	// }
	// return (IProgramElement) stack.peek();
	// }

	public boolean visit(MethodDeclaration methodDeclaration, ClassScope scope) {
		IProgramElement peNode = null;
		// For intertype decls, use the modifiers from the original signature,
		// not the generated method

		if (methodDeclaration instanceof InterTypeDeclaration) {
			InterTypeDeclaration itd = (InterTypeDeclaration) methodDeclaration;
			ResolvedMember sig = itd.getSignature();
			peNode = new ProgramElement(activeStructureModel, null, IProgramElement.Kind.ERROR, makeLocation(methodDeclaration),
					(sig != null ? sig.getModifiers() : 0), null, null);

		} else {
			peNode = new ProgramElement(activeStructureModel, null, IProgramElement.Kind.ERROR, makeLocation(methodDeclaration),
					methodDeclaration.modifiers, null, null);
		}
		formatter.genLabelAndKind(methodDeclaration, peNode); // will set the
		// name
		genBytecodeInfo(methodDeclaration, peNode);
		List namedPointcuts = genNamedPointcuts(methodDeclaration);
		// if (shouldAddUsesPointcut)
		// addUsesPointcutRelationsForNode(peNode, namedPointcuts, methodDeclaration);

		if (methodDeclaration instanceof DeclareDeclaration) {
			DeclareDeclaration dDeclaration = (DeclareDeclaration) methodDeclaration;
			Declare decl = dDeclaration.declareDecl;
			if (decl instanceof DeclareParents) {
				TypePatternList tpl = ((DeclareParents) decl).getParents();
				List<String> parents = new ArrayList<String>();
				for (int i = 0; i < tpl.size(); i++) {
					parents.add(tpl.get(i).getExactType().getName().replaceAll("\\$", "."));
				}
				peNode.setParentTypes(parents);
			}
			if (decl instanceof DeclareAnnotation) {
				DeclareAnnotation da = (DeclareAnnotation) decl;
				ResolvedType annotationType = da.getAnnotationType();
				if (annotationType == null) {
					String s = ((DeclareAnnotation) decl).getAnnotationString();
					if (s != null && s.length() > 0) {
						s = s.substring(1);
					}
					peNode.setAnnotationType(s);
				} else {
					peNode.setAnnotationType(annotationType.getName());
				}
				if (da.isRemover()) {
					peNode.setAnnotationRemover(true);
				}
			}
		}
		if (methodDeclaration.returnType != null) {
			// if we don't make the distinction between ITD fields and other
			// methods, then we loose the type, for example int, for the field
			// and instead get "void".
			if (peNode.getKind().equals(IProgramElement.Kind.INTER_TYPE_FIELD)) {

				InterTypeFieldDeclaration itfd = (InterTypeFieldDeclaration) methodDeclaration;
				if (itfd.getRealFieldType() != null) {
					peNode.setCorrespondingType(new String(itfd.getRealFieldType().readableName()));
				} else {
					peNode.setCorrespondingType(null);
				}

				// was peNode.setCorrespondingType(methodDeclaration.returnType.toString());
			} else {
				if (methodDeclaration.returnType.resolvedType != null) {
					peNode.setCorrespondingType(new String(methodDeclaration.returnType.resolvedType.readableName()));
				} else {
					peNode.setCorrespondingType(null);
				}
			}
		} else {
			peNode.setCorrespondingType(null);
		}
		peNode.setSourceSignature(genSourceSignature(methodDeclaration));
		peNode.setFormalComment(generateJavadocComment(methodDeclaration));

		// TODO: add return type test
		if (peNode.getKind().equals(IProgramElement.Kind.METHOD)) {
			if ((peNode.getName().charAt(0) == 'm')
					&& (peNode.toLabelString().equals("main(String[])") || peNode.toLabelString()
							.equals("main(java.lang.String[])"))
					&& peNode.getModifiers().contains(IProgramElement.Modifiers.STATIC)
					&& peNode.getAccessibility().equals(IProgramElement.Accessibility.PUBLIC)) {
				((IProgramElement) stack.peek()).setRunnable(true);
			}
		}

		stack.push(peNode);
		return true;
	}

	// private void addUsesPointcutRelationsForNode(IProgramElement peNode, List namedPointcuts, MethodDeclaration declaration) {
	// for (Iterator it = namedPointcuts.iterator(); it.hasNext();) {
	// ReferencePointcut rp = (ReferencePointcut) it.next();
	// ResolvedMember member = getPointcutDeclaration(rp, declaration);
	// if (member != null) {
	// IRelationship foreward = AsmManager.getDefault().getRelationshipMap().get(peNode.getHandleIdentifier(),
	// IRelationship.Kind.USES_POINTCUT, "uses pointcut", false, true);
	// IProgramElement forwardIPE = AsmManager.getDefault().getHierarchy().findElementForSourceLine(
	// member.getSourceLocation());
	// foreward.addTarget(AsmManager.getDefault().getHandleProvider().createHandleIdentifier(forwardIPE));
	//
	// IRelationship back = AsmManager.getDefault().getRelationshipMap().get(
	// AsmManager.getDefault().getHandleProvider().createHandleIdentifier(forwardIPE),
	// IRelationship.Kind.USES_POINTCUT, "pointcut used by", false, true);
	// back.addTarget(peNode.getHandleIdentifier());
	// }
	// }
	// }

	private ResolvedMember getPointcutDeclaration(ReferencePointcut rp, MethodDeclaration declaration) {
		EclipseFactory factory = ((AjLookupEnvironment) declaration.scope.environment()).factory;
		World world = factory.getWorld();
		UnresolvedType onType = rp.onType;
		if (onType == null) {
			if (declaration.binding != null) {
				Member member = factory.makeResolvedMember(declaration.binding);
				onType = member.getDeclaringType();
			} else {
				return null;
			}
		}
		ResolvedMember[] members = onType.resolve(world).getDeclaredPointcuts();
		if (members != null) {
			for (int i = 0; i < members.length; i++) {
				if (members[i].getName().equals(rp.name)) {
					return members[i];
				}
			}
		}
		return null;
	}

	/**
	 * @param methodDeclaration
	 * @return all of the named pointcuts referenced by the PCD of this declaration
	 */
	private List genNamedPointcuts(MethodDeclaration methodDeclaration) {
		List pointcuts = new ArrayList();
		if (methodDeclaration instanceof AdviceDeclaration) {
			if (((AdviceDeclaration) methodDeclaration).pointcutDesignator != null) {
				addAllNamed(((AdviceDeclaration) methodDeclaration).pointcutDesignator.getPointcut(), pointcuts);
			}
		} else if (methodDeclaration instanceof PointcutDeclaration) {
			if (((PointcutDeclaration) methodDeclaration).pointcutDesignator != null) {
				addAllNamed(((PointcutDeclaration) methodDeclaration).pointcutDesignator.getPointcut(), pointcuts);
			}
		}
		return pointcuts;
	}

	/**
	 * @param left
	 * @param pointcuts
	 *            accumulator for named pointcuts
	 */
	private void addAllNamed(Pointcut pointcut, List pointcuts) {
		if (pointcut == null) {
			return;
		}
		if (pointcut instanceof ReferencePointcut) {
			ReferencePointcut rp = (ReferencePointcut) pointcut;
			pointcuts.add(rp);
		} else if (pointcut instanceof AndPointcut) {
			AndPointcut ap = (AndPointcut) pointcut;
			addAllNamed(ap.getLeft(), pointcuts);
			addAllNamed(ap.getRight(), pointcuts);
		} else if (pointcut instanceof OrPointcut) {
			OrPointcut op = (OrPointcut) pointcut;
			addAllNamed(op.getLeft(), pointcuts);
			addAllNamed(op.getRight(), pointcuts);
		}
	}

	private String genSourceSignature(MethodDeclaration methodDeclaration) {
		StringBuffer output = new StringBuffer();
		ASTNode.printModifiers(methodDeclaration.modifiers, output);

		// Append Type Parameters if any
		TypeParameter types[] = methodDeclaration.typeParameters();
		if (types != null && types.length != 0) {
			output.append("<");
			for (int i = 0; i < types.length; i++) {
				if (i > 0) {
					output.append(", ");
				}
				types[i].printStatement(0, output);
			}
			output.append("> ");
		}
		String methodName = methodDeclaration.selector==null?"null":new String(methodDeclaration.selector);

		methodDeclaration.printReturnType(0, output).append(methodName).append('(');
		if (methodDeclaration.arguments != null) {
			for (int i = 0; i < methodDeclaration.arguments.length; i++) {
				if (i > 0) {
					output.append(", "); //$NON-NLS-1$
				}
				methodDeclaration.arguments[i].print(0, output);
			}
		}
		output.append(')');
		if (methodDeclaration.thrownExceptions != null) {
			output.append(" throws "); //$NON-NLS-1$
			for (int i = 0; i < methodDeclaration.thrownExceptions.length; i++) {
				if (i > 0) {
					output.append(", "); //$NON-NLS-1$
				}
				methodDeclaration.thrownExceptions[i].print(0, output);
			}
		}
		return output.toString();
	}

	// protected void genBytecodeInfo(MethodDeclaration methodDeclaration,
	// IProgramElement peNode) {
	// if (methodDeclaration.binding != null) {
	// String memberName = "";
	// String memberBytecodeSignature = "";
	// try {
	// EclipseFactory factory =
	// ((AjLookupEnvironment)methodDeclaration.scope.environment()).factory;
	// Member member = factory.makeResolvedMember(methodDeclaration.binding);
	// memberName = member.getName();
	// memberBytecodeSignature = member.getSignature();
	// } catch (BCException bce) { // bad type name
	// memberName = "<undefined>";
	// } catch (NullPointerException npe) {
	// memberName = "<undefined>";
	// }
	//
	// peNode.setBytecodeName(memberName);
	// peNode.setBytecodeSignature(memberBytecodeSignature);
	// }
	// ((IProgramElement)stack.peek()).addChild(peNode);
	// }
	protected void genBytecodeInfo(MethodDeclaration methodDeclaration, IProgramElement peNode) {
		if (methodDeclaration.binding != null) {
			try {
				EclipseFactory factory = ((AjLookupEnvironment) methodDeclaration.scope.environment()).factory;
				Member member = factory.makeResolvedMember(methodDeclaration.binding);
				peNode.setBytecodeName(member.getName());
				peNode.setBytecodeSignature(member.getSignature());
			} catch (BCException bce) { // bad type name
				bce.printStackTrace();
			} catch (NullPointerException npe) {
				npe.printStackTrace();
			}
		}
		((IProgramElement) stack.peek()).addChild(peNode);
	}

	public void endVisit(MethodDeclaration methodDeclaration, ClassScope scope) {
		stack.pop();
	}

	public boolean visit(ImportReference importRef, CompilationUnitScope scope) {
		// 3.3 compiler used to represent the package statement in such a way that toString() would return 'foo.*' for 'package foo;'
		// 3.7 compiler doesn't create an 'ondemand' import reference so it is purely toString()'d as 'foo'
		String currPackageImport = importRef.toString();
		String stackPackageName = stack.peek().getPackageName();
		if (stackPackageName.equals(currPackageImport)) {
			packageDecl = importRef;
		} else {

			ProgramElement peNode = new ProgramElement(activeStructureModel, new String(importRef.toString()),
					IProgramElement.Kind.IMPORT_REFERENCE, makeLocation(importRef), 0,// could set static here, but for
					// some reason the info is
					// private
					null, null);
			// set it here instead
			if (importRef.isStatic()) {
				peNode.addModifiers(IProgramElement.Modifiers.STATIC);
			}
			// create Source signature for import
			peNode.setSourceSignature(genSourceSignature(importRef));

			IProgramElement containingTypeElement = (IProgramElement) stack.peek();
			ProgramElement imports = getImportReferencesRoot();
			imports.addChild(0, peNode);
			stack.push(peNode);
		}
		return true;
	}

	private ProgramElement getImportReferencesRoot() {
		IProgramElement element = (IProgramElement) stack.peek();
		boolean hasPackageDeclaration = (element.getChildren().get(0)).getKind().isPackageDeclaration();
		return (ProgramElement) element.getChildren().get(hasPackageDeclaration ? 1 : 0);
	}

	public void endVisit(ImportReference importRef, CompilationUnitScope scope) {
		// 3.3 compiler used to represent the package statement in such a way that toString() would return 'foo.*' for 'package foo;'
		// 3.7 compiler doesn't create an 'ondemand' import reference so it is purely toString()'d as 'foo'
		String currPackageImport = importRef.toString();
		if (!stack.peek().getPackageName().equals(currPackageImport)) {
			stack.pop();
		}
	}

	private String genSourceSignature(ImportReference importreference) {
		StringBuffer output = new StringBuffer();
		output.append("import ");
		ASTNode.printModifiers(importreference.modifiers, output);
		output.append(importreference);
		output.append(";");
		return output.toString();
	}

	public boolean visit(FieldDeclaration fieldDeclaration, MethodScope scope) {
		IProgramElement peNode = null;
		if (fieldDeclaration.type == null) { // The field represents an enum
			// value
			peNode = new ProgramElement(activeStructureModel, new String(fieldDeclaration.name), IProgramElement.Kind.ENUM_VALUE,
					makeLocation(fieldDeclaration), fieldDeclaration.modifiers, null, null);
			String type =  null;
			boolean isOk = true;
			if (fieldDeclaration.binding == null) {
				type="fieldDeclaration_binding_is_null";
				System.err.println("DebugFor402832: null fieldDeclaration.binding for "+fieldDeclaration);
				isOk=false;
			} else {
				if (fieldDeclaration.binding.type==null) {
					System.err.println("DebugFor402832: null fieldDeclaration.binding.type for "+fieldDeclaration);
					type="fieldDeclaration_binding_type_is_null";
					isOk=false;				
				} else {
					type=fieldDeclaration.binding.type.debugName();
				}
			}
			if (!isOk) {
				if (fieldDeclaration.type!=null && fieldDeclaration.type.resolvedType!=null) {
					type = fieldDeclaration.type.resolvedType.debugName();
					System.err.println("DebugFor402832: used secondary route to compute name for "+fieldDeclaration+", set to "+type);
					isOk=true;
				}
			}
			peNode.setCorrespondingType(type);
//			peNode.setCorrespondingType(fieldDeclaration.binding.type.debugName());
		} else {
			peNode = new ProgramElement(activeStructureModel, new String(fieldDeclaration.name), IProgramElement.Kind.FIELD,
					makeLocation(fieldDeclaration), fieldDeclaration.modifiers, null, null);

			if (fieldDeclaration.type.resolvedType != null) {
				char[] cs = fieldDeclaration.type.resolvedType.readableName();
				// fieldDeclaration.type.resolvedType.genericTypeSignature()
				peNode.setCorrespondingType(new String(cs));
			} else {
				// peNode.setCorrespondingType(null);
				peNode.setCorrespondingType(fieldDeclaration.type.toString());
			}
		}
		peNode.setSourceSignature(genSourceSignature(fieldDeclaration));
		peNode.setFormalComment(generateJavadocComment(fieldDeclaration));
		// peNode.setBytecodeSignature(new String(fieldDeclaration.binding.type.signature()));

		((IProgramElement) stack.peek()).addChild(peNode);
		stack.push(peNode);
		return true;
	}

	public void endVisit(FieldDeclaration fieldDeclaration, MethodScope scope) {
		stack.pop();
	}

	/**
	 * Checks if comments should be added to the model before generating.
	 */
	protected String generateJavadocComment(ASTNode astNode) {
		if (buildConfig != null && !buildConfig.isGenerateJavadocsInModelMode()) {
			return null;
		}

		// StringBuffer sb = new StringBuffer(); // !!! specify length?
		// boolean completed = false;
		int startIndex = -1;
		if (astNode instanceof MethodDeclaration) {
			startIndex = ((MethodDeclaration) astNode).declarationSourceStart;
		} else if (astNode instanceof FieldDeclaration) {
			startIndex = ((FieldDeclaration) astNode).declarationSourceStart;
		} else if (astNode instanceof TypeDeclaration) {
			startIndex = ((TypeDeclaration) astNode).declarationSourceStart;
		} else if (astNode instanceof ConstructorDeclaration) {
			startIndex = ((ConstructorDeclaration) astNode).declarationSourceStart;
		}

		if (startIndex == -1) {
			return null;
		} else if (currCompilationResult.compilationUnit.getContents()[startIndex] == '/') {
			char[] comment = CharOperation.subarray(currCompilationResult.compilationUnit.getContents(), startIndex,
					astNode.sourceStart);
			while (comment.length > 2) {
				int star = CharOperation.indexOf('*', comment);
				if (star == -1) {
					return null;
				}
				// looking for '/**' and not '//' or '//*'
				if (star != 0 && (comment[star - 1] == '/') && (comment[star + 1] == '*')
						&& (star - 2 < 0 || comment[star - 2] != '/')) {
					boolean completed = false;
					StringBuffer sb = new StringBuffer();
					for (int i = 0; i < comment.length && !completed; i++) {
						char curr = comment[i];
						if (curr == '/' && sb.length() > 2 && sb.charAt(sb.length() - 1) == '*') {
							completed = true; // found */
						}
						sb.append(comment[i]);
					}
					// The following will remove any non-javadoc comments
					// preceeding a javadoc comment in this block
					if (sb.toString().indexOf("/**") != 0) {
						return sb.toString().substring(sb.toString().indexOf("/**"));
					}
					return sb.toString();
				}
				comment = CharOperation.subarray(comment, star + 1, comment.length);
			}
		}
		return null;
	}

	/**
	 * 
	 */
	protected String genSourceSignature(FieldDeclaration fieldDeclaration) {
		StringBuffer output = new StringBuffer();
		if (fieldDeclaration.type == null) { // This is an enum value
			output.append(fieldDeclaration.name); // the "," or ";" has to be
			// put on by whatever uses
			// the sourceSignature
			return output.toString();
		} else {
			FieldDeclaration.printModifiers(fieldDeclaration.modifiers, output);
			fieldDeclaration.type.print(0, output).append(' ').append(fieldDeclaration.name);
		}

		output.append(" = ");
		if (fieldDeclaration.initialization != null
				&& (fieldDeclaration.initialization instanceof Literal
						|| fieldDeclaration.initialization instanceof OperatorExpression || fieldDeclaration.initialization instanceof Reference)) {
			fieldDeclaration.initialization.printExpression(0, output);
		} else {
			output.append("null");
		}
		output.append(";\n");
		return output.toString();
	}

	// public boolean visit(ImportReference importRef, CompilationUnitScope
	// scope) {
	// ProgramElementNode peNode = new ProgramElementNode(
	// new String(importRef.toString()),
	// ProgramElementNode.Kind.,
	// makeLocation(importRef),
	// 0,
	// "",
	// new ArrayList());
	// ((IProgramElement)stack.peek()).addChild(0, peNode);
	// stack.push(peNode);
	// return true;
	// }
	// public void endVisit(ImportReference importRef,CompilationUnitScope
	// scope) {
	// stack.pop();
	// }

	public boolean visit(ConstructorDeclaration constructorDeclaration, ClassScope scope) {
		if ((constructorDeclaration.bits & ASTNode.IsDefaultConstructor) != 0) {
			stack.push(null); // a little weird but does the job
			return true;
		}
		StringBuffer argumentsSignature = new StringBuffer();
		argumentsSignature.append("(");
		if (constructorDeclaration.arguments != null) {
			for (int i = 0; i < constructorDeclaration.arguments.length; i++) {
				argumentsSignature.append(constructorDeclaration.arguments[i].type);
				if (i + 1 < constructorDeclaration.arguments.length) {
					argumentsSignature.append(",");
				}
			}
		}
		argumentsSignature.append(")");
		IProgramElement peNode = new ProgramElement(activeStructureModel, new String(constructorDeclaration.selector),
				IProgramElement.Kind.CONSTRUCTOR, makeLocation(constructorDeclaration), constructorDeclaration.modifiers, null,
				null);
		formatter.setParameters(constructorDeclaration, peNode);
		peNode.setModifiers(constructorDeclaration.modifiers);
		peNode.setSourceSignature(genSourceSignature(constructorDeclaration));
		peNode.setFormalComment(generateJavadocComment(constructorDeclaration));

		// Fix to enable us to anchor things from ctor nodes
		if (constructorDeclaration.binding != null) {
			String memberName = "";
			String memberBytecodeSignature = "";
			try {
				EclipseFactory factory = ((AjLookupEnvironment) constructorDeclaration.scope.environment()).factory;
				Member member = factory.makeResolvedMember(constructorDeclaration.binding);
				memberName = member.getName();
				memberBytecodeSignature = member.getSignature();
			} catch (BCException bce) { // bad type name
				memberName = "<undefined>";
			} catch (NullPointerException npe) {
				memberName = "<undefined>";
			}
			peNode.setBytecodeName(memberName);
			peNode.setBytecodeSignature(memberBytecodeSignature);
		}

		((IProgramElement) stack.peek()).addChild(peNode);
		stack.push(peNode);
		return true;
	}

	public void endVisit(ConstructorDeclaration constructorDeclaration, ClassScope scope) {
		stack.pop();
	}

	private String genSourceSignature(ConstructorDeclaration constructorDeclaration) {
		StringBuffer output = new StringBuffer();
		ASTNode.printModifiers(constructorDeclaration.modifiers, output);

		// Append Type Parameters if any
		TypeParameter types[] = constructorDeclaration.typeParameters();
		if (types != null && types.length != 0) {
			output.append("<");
			for (int i = 0; i < types.length; i++) {
				if (i > 0) {
					output.append(", ");
				}
				types[i].printStatement(0, output);
			}
			output.append("> ");
		}

		output.append(constructorDeclaration.selector).append('(');
		if (constructorDeclaration.arguments != null) {
			for (int i = 0; i < constructorDeclaration.arguments.length; i++) {
				if (i > 0) {
					output.append(", "); //$NON-NLS-1$
				}
				constructorDeclaration.arguments[i].print(0, output);
			}
		}
		output.append(')');
		if (constructorDeclaration.thrownExceptions != null) {
			output.append(" throws "); //$NON-NLS-1$
			for (int i = 0; i < constructorDeclaration.thrownExceptions.length; i++) {
				if (i > 0) {
					output.append(", "); //$NON-NLS-1$
				}
				constructorDeclaration.thrownExceptions[i].print(0, output);
			}
		}
		return output.toString();
	}

	// public boolean visit(Clinit clinit, ClassScope scope) {
	// ProgramElementNode peNode = new ProgramElementNode(
	// "<clinit>",
	// ProgramElementNode.Kind.INITIALIZER,
	// makeLocation(clinit),
	// clinit.modifiers,
	// "",
	// new ArrayList());
	// ((IProgramElement)stack.peek()).addChild(peNode);
	// stack.push(peNode);
	// return false;
	// }
	// public void endVisit(Clinit clinit, ClassScope scope) {
	// stack.pop();
	// }

	/**
	 * This method works-around an odd traverse implementation on Initializer
	 */
	private Initializer inInitializer = null;

	public boolean visit(Initializer initializer, MethodScope scope) {
		if (initializer == inInitializer) {
			return false;
		}
		inInitializer = initializer;

		IProgramElement peNode = new ProgramElement(activeStructureModel, "...", IProgramElement.Kind.INITIALIZER,
				makeLocation(initializer), initializer.modifiers, null, null);
		// "",
		// new ArrayList());
		((IProgramElement) stack.peek()).addChild(peNode);
		stack.push(peNode);
		initializer.block.traverse(this, scope);
		stack.pop();
		inInitializer = null;
		return false;
	}

	// ??? handle non-existant files
	protected ISourceLocation makeLocation(ASTNode node) {
		String fileName = "";
		if (filename != null) {
			fileName = filename;
		}
		// AMC - different strategies based on node kind
		int startLine = getStartLine(node);
		int endLine = getEndLine(node);
		SourceLocation loc = null;
		if (startLine <= endLine) {
			// found a valid end line for this node...
			loc = new SourceLocation(new File(fileName), startLine, endLine);
			loc.setOffset(node.sourceStart);
		} else {
			loc = new SourceLocation(new File(fileName), startLine);
			loc.setOffset(node.sourceStart);
		}
		return loc;
	}

	// AMC - overloaded set of methods to get start and end lines for
	// various ASTNode types. They have no common ancestor in the
	// hierarchy!!
	protected int getStartLine(ASTNode n) {
		// if ( n instanceof AbstractVariableDeclaration ) return getStartLine(
		// (AbstractVariableDeclaration)n);
		// if ( n instanceof AbstractMethodDeclaration ) return getStartLine(
		// (AbstractMethodDeclaration)n);
		// if ( n instanceof TypeDeclaration ) return getStartLine(
		// (TypeDeclaration)n);
		return Util.getLineNumber(n.sourceStart, lineseps, 0, lineseps.length - 1);
		// return ProblemHandler.searchLineNumber(lineseps,
		// currCompilationResult.lineSeparatorPositions,
		// n.sourceStart);
	}

	// AMC - overloaded set of methods to get start and end lines for
	// various ASTNode types. They have no common ancestor in the
	// hierarchy!!
	protected int getEndLine(ASTNode n) {
		if (n instanceof AbstractVariableDeclaration) {
			return getEndLine((AbstractVariableDeclaration) n);
		}
		if (n instanceof AbstractMethodDeclaration) {
			return getEndLine((AbstractMethodDeclaration) n);
		}
		if (n instanceof TypeDeclaration) {
			return getEndLine((TypeDeclaration) n);
		}
		return Util.getLineNumber(n.sourceEnd, lineseps, 0, lineseps.length - 1);
		// return ProblemHandler.searchLineNumber(lineseps,
		// currCompilationResult.lineSeparatorPositions,
		// n.sourceEnd);
	}

	// AMC - overloaded set of methods to get start and end lines for
	// various ASTNode types. They have no common ancestor in the
	// hierarchy!!
	// private int getStartLine( AbstractVariableDeclaration avd ) {
	// return ProblemHandler.searchLineNumber(
	// currCompilationResult.lineSeparatorPositions,
	// avd.declarationSourceStart);
	// }

	// AMC - overloaded set of methods to get start and end lines for
	// various ASTNode types. They have no common ancestor in the
	// hierarchy!!
	private int getEndLine(AbstractVariableDeclaration avd) {
		return Util.getLineNumber(avd.declarationSourceEnd, lineseps, 0, lineseps.length - 1);
	}

	// AMC - overloaded set of methods to get start and end lines for
	// various ASTNode types. They have no common ancestor in the
	// hierarchy!!
	// private int getStartLine( AbstractMethodDeclaration amd ){
	// return ProblemHandler.searchLineNumber(
	// currCompilationResult.lineSeparatorPositions,
	// amd.declarationSourceStart);
	// }

	// AMC - overloaded set of methods to get start and end lines for
	// various ASTNode types. They have no common ancestor in the
	// hierarchy!!
	private int getEndLine(AbstractMethodDeclaration amd) {
		return Util.getLineNumber(amd.declarationSourceEnd, lineseps, 0, lineseps.length - 1);
	}

	// AMC - overloaded set of methods to get start and end lines for
	// various ASTNode types. They have no common ancestor in the
	// hierarchy!!
	// private int getStartLine( TypeDeclaration td ){
	// return ProblemHandler.searchLineNumber(
	// currCompilationResult.lineSeparatorPositions,
	// td.declarationSourceStart);
	// }

	// AMC - overloaded set of methods to get start and end lines for
	// various ASTNode types. They have no common ancestor in the
	// hierarchy!!
	private int getEndLine(TypeDeclaration td) {
		return Util.getLineNumber(td.declarationSourceEnd, lineseps, 0, lineseps.length - 1);
	}
}
