| /******************************************************************************* |
| * Copyright (c) 2000, 2004 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Common Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/cpl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.core; |
| |
| import java.util.Map; |
| import java.util.Stack; |
| |
| import org.eclipse.jdt.core.*; |
| import org.eclipse.jdt.core.ICompilationUnit; |
| import org.eclipse.jdt.core.IJavaElement; |
| import org.eclipse.jdt.core.Signature; |
| import org.eclipse.jdt.core.compiler.CharOperation; |
| import org.eclipse.jdt.core.compiler.IProblem; |
| import org.eclipse.jdt.internal.compiler.ISourceElementRequestor; |
| import org.eclipse.jdt.internal.compiler.parser.Parser; |
| import org.eclipse.jdt.internal.compiler.util.HashtableOfObject; |
| import org.eclipse.jdt.internal.core.util.ReferenceInfoAdapter; |
| |
| /** |
| * A requestor for the fuzzy parser, used to compute the children of an ICompilationUnit. |
| */ |
| 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 JavaElementInfo importContainerInfo = null; |
| |
| /** |
| * 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; |
| |
| /** |
| * 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; |
| |
| /** |
| * 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 name of the source file being parsed. |
| */ |
| protected char[] sourceFileName= null; |
| |
| /** |
| * The dot-separated name of the package the compilation unit |
| * is contained in - based on the package statement in the |
| * compilation unit, and initialized by #acceptPackage. |
| * Initialized to <code>null</code> for the default package. |
| */ |
| protected char[] packageName= null; |
| |
| /** |
| * 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; |
| |
| /** |
| * Empty collections used for efficient initialization |
| */ |
| protected static String[] NO_STRINGS = new String[0]; |
| protected static byte[] NO_BYTES= new byte[]{}; |
| |
| 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.sourceFileName= unit.getPath().toString().toCharArray(); |
| } |
| /** |
| * @see ISourceElementRequestor |
| */ |
| public void acceptImport(int declarationStart, int declarationEnd, char[] name, boolean onDemand, int modifiers) { |
| JavaElementInfo parentInfo = (JavaElementInfo) this.infoStack.peek(); |
| 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 |
| ImportContainer importContainer= (ImportContainer)parentCU.getImportContainer(); |
| if (this.importContainerInfo == null) { |
| this.importContainerInfo= new JavaElementInfo(); |
| parentInfo.addChild(importContainer); |
| this.newElements.put(importContainer, this.importContainerInfo); |
| } |
| |
| // tack on the '.*' if it is onDemand |
| String importName; |
| if (onDemand) { |
| importName= new String(name) + ".*"; //$NON-NLS-1$ |
| } else { |
| importName= new String(name); |
| } |
| |
| ImportDeclaration handle = new ImportDeclaration(importContainer, importName); |
| resolveDuplicates(handle); |
| |
| ImportDeclarationElementInfo info = new ImportDeclarationElementInfo(); |
| info.setSourceRangeStart(declarationStart); |
| info.setSourceRangeEnd(declarationEnd); |
| info.setFlags(modifiers); |
| info.name = name; // no trailing * if onDemand |
| info.setOnDemand(onDemand); |
| |
| this.importContainerInfo.addChild(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, |
| * |
| */ |
| public void acceptLineSeparatorPositions(int[] positions) { |
| // ignore line separator positions |
| } |
| /** |
| * @see ISourceElementRequestor |
| */ |
| public void acceptPackage(int declarationStart, int declarationEnd, char[] name) { |
| |
| JavaElementInfo parentInfo = (JavaElementInfo) this.infoStack.peek(); |
| JavaElement parentHandle= (JavaElement) this.handleStack.peek(); |
| PackageDeclaration handle = null; |
| this.packageName= name; |
| |
| if (parentHandle.getElementType() == IJavaElement.COMPILATION_UNIT) { |
| handle = new PackageDeclaration((CompilationUnit) parentHandle, new String(name)); |
| } |
| else { |
| Assert.isTrue(false); // Should not happen |
| } |
| resolveDuplicates(handle); |
| |
| SourceRefElementInfo info = new SourceRefElementInfo(); |
| info.setSourceRangeStart(declarationStart); |
| info.setSourceRangeEnd(declarationEnd); |
| |
| parentInfo.addChild(handle); |
| this.newElements.put(handle, info); |
| |
| } |
| public void acceptProblem(IProblem problem) { |
| if ((problem.getID() & IProblem.Syntax) != 0){ |
| this.hasSyntaxErrors = true; |
| } |
| } |
| /** |
| * Convert these type names to signatures. |
| * @see Signature |
| */ |
| /* default */ static String[] convertTypeNamesToSigs(char[][] typeNames) { |
| if (typeNames == null) |
| return NO_STRINGS; |
| int n = typeNames.length; |
| if (n == 0) |
| return NO_STRINGS; |
| String[] typeSigs = new String[n]; |
| for (int i = 0; i < n; ++i) { |
| typeSigs[i] = Signature.createTypeSignature(typeNames[i], false); |
| } |
| return typeSigs; |
| } |
| /** |
| * @see ISourceElementRequestor |
| */ |
| public void enterCompilationUnit() { |
| this.infoStack = new Stack(); |
| this.handleStack= new Stack(); |
| this.infoStack.push(this.unitInfo); |
| this.handleStack.push(this.unit); |
| } |
| /** |
| * @see ISourceElementRequestor |
| */ |
| public void enterConstructor(MethodInfo methodInfo) { |
| enterMethod(methodInfo); |
| } |
| /** |
| * @see ISourceElementRequestor |
| */ |
| public void enterField(FieldInfo fieldInfo) { |
| |
| SourceTypeElementInfo parentInfo = (SourceTypeElementInfo) this.infoStack.peek(); |
| JavaElement parentHandle= (JavaElement) this.handleStack.peek(); |
| SourceField handle = null; |
| |
| if (parentHandle.getElementType() == IJavaElement.TYPE) { |
| handle = new SourceField(parentHandle, new String(fieldInfo.name)); |
| } |
| else { |
| Assert.isTrue(false); // Should not happen |
| } |
| resolveDuplicates(handle); |
| |
| SourceFieldElementInfo info = new SourceFieldElementInfo(); |
| info.fieldName = fieldInfo.name; |
| info.setNameSourceStart(fieldInfo.nameSourceStart); |
| info.setNameSourceEnd(fieldInfo.nameSourceEnd); |
| info.setSourceRangeStart(fieldInfo.declarationStart); |
| info.setFlags(fieldInfo.modifiers); |
| info.setTypeName(fieldInfo.type); |
| |
| this.unitInfo.addAnnotationPositions(handle, fieldInfo.annotationPositions); |
| |
| parentInfo.addChild(handle); |
| this.newElements.put(handle, info); |
| |
| this.infoStack.push(info); |
| this.handleStack.push(handle); |
| } |
| /** |
| * @see ISourceElementRequestor |
| */ |
| public void enterInitializer( |
| int declarationSourceStart, |
| int modifiers) { |
| JavaElementInfo parentInfo = (JavaElementInfo) this.infoStack.peek(); |
| JavaElement parentHandle= (JavaElement) this.handleStack.peek(); |
| Initializer handle = null; |
| |
| if (parentHandle.getElementType() == IJavaElement.TYPE) { |
| handle = new Initializer(parentHandle, 1); |
| } |
| else { |
| Assert.isTrue(false); // Should not happen |
| } |
| resolveDuplicates(handle); |
| |
| InitializerElementInfo info = new InitializerElementInfo(); |
| info.setSourceRangeStart(declarationSourceStart); |
| info.setFlags(modifiers); |
| |
| parentInfo.addChild(handle); |
| this.newElements.put(handle, info); |
| |
| this.infoStack.push(info); |
| this.handleStack.push(handle); |
| } |
| /** |
| * @see ISourceElementRequestor |
| */ |
| public void enterMethod(MethodInfo methodInfo) { |
| |
| SourceTypeElementInfo parentInfo = (SourceTypeElementInfo) 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; |
| } |
| |
| String[] parameterTypeSigs = convertTypeNamesToSigs(methodInfo.parameterTypes); |
| if (parentHandle.getElementType() == IJavaElement.TYPE) { |
| handle = new SourceMethod(parentHandle, new String(methodInfo.name), parameterTypeSigs); |
| } |
| else { |
| Assert.isTrue(false); // Should not happen |
| } |
| resolveDuplicates(handle); |
| |
| SourceMethodElementInfo info; |
| if (methodInfo.isConstructor) |
| info = new SourceConstructorInfo(); |
| else if (methodInfo.isAnnotation) |
| info = new SourceAnnotationMethodInfo(); |
| else |
| info = new SourceMethodInfo(); |
| info.setSourceRangeStart(methodInfo.declarationStart); |
| int flags = methodInfo.modifiers; |
| info.selector = methodInfo.name; |
| info.setNameSourceStart(methodInfo.nameSourceStart); |
| info.setNameSourceEnd(methodInfo.nameSourceEnd); |
| info.setFlags(flags); |
| info.setArgumentNames(methodInfo.parameterNames); |
| info.setArgumentTypeNames(methodInfo.parameterTypes); |
| info.setReturnType(methodInfo.returnType == null ? new char[]{'v', 'o','i', 'd'} : methodInfo.returnType); |
| info.setExceptionTypeNames(methodInfo.exceptionTypes); |
| this.unitInfo.addAnnotationPositions(handle, methodInfo.annotationPositions); |
| parentInfo.addChild(handle); |
| this.newElements.put(handle, info); |
| this.infoStack.push(info); |
| this.handleStack.push(handle); |
| |
| if (methodInfo.typeParameters != null) { |
| for (int i = 0, length = methodInfo.typeParameters.length; i < length; i++) { |
| TypeParameterInfo typeParameterInfo = methodInfo.typeParameters[i]; |
| enterTypeParameter(typeParameterInfo); |
| exitMember(typeParameterInfo.declarationEnd); |
| } |
| } |
| } |
| /** |
| * @see ISourceElementRequestor |
| */ |
| public void enterType(TypeInfo typeInfo) { |
| |
| JavaElementInfo parentInfo = (JavaElementInfo) this.infoStack.peek(); |
| JavaElement parentHandle= (JavaElement) this.handleStack.peek(); |
| String nameString= new String(typeInfo.name); |
| SourceType handle = handle = new SourceType(parentHandle, nameString); //NB: occurenceCount is computed in resolveDuplicates |
| resolveDuplicates(handle); |
| |
| SourceTypeElementInfo info = new SourceTypeElementInfo(); |
| info.setHandle(handle); |
| info.setSourceRangeStart(typeInfo.declarationStart); |
| info.setFlags(typeInfo.modifiers); |
| info.setNameSourceStart(typeInfo.nameSourceStart); |
| info.setNameSourceEnd(typeInfo.nameSourceEnd); |
| info.setSuperclassName(typeInfo.superclass); |
| info.setSuperInterfaceNames(typeInfo.superinterfaces); |
| info.setSourceFileName(this.sourceFileName); |
| info.setPackageName(this.packageName); |
| parentInfo.addChild(handle); |
| this.unitInfo.addAnnotationPositions(handle, typeInfo.annotationPositions); |
| this.newElements.put(handle, info); |
| this.infoStack.push(info); |
| this.handleStack.push(handle); |
| |
| if (typeInfo.typeParameters != null) { |
| for (int i = 0, length = typeInfo.typeParameters.length; i < length; i++) { |
| TypeParameterInfo typeParameterInfo = typeInfo.typeParameters[i]; |
| enterTypeParameter(typeParameterInfo); |
| exitMember(typeParameterInfo.declarationEnd); |
| } |
| } |
| } |
| protected void enterTypeParameter(TypeParameterInfo typeParameterInfo) { |
| JavaElementInfo parentInfo = (JavaElementInfo) this.infoStack.peek(); |
| JavaElement parentHandle = (JavaElement) this.handleStack.peek(); |
| String nameString = new String(typeParameterInfo.name); |
| TypeParameter handle = handle = new TypeParameter(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.unitInfo.addAnnotationPositions(handle, typeParameterInfo.annotationPositions); |
| this.newElements.put(handle, info); |
| this.infoStack.push(info); |
| this.handleStack.push(handle); |
| } |
| /** |
| * @see ISourceElementRequestor |
| */ |
| public void exitCompilationUnit(int declarationEnd) { |
| this.unitInfo.setSourceLength(declarationEnd + 1); |
| |
| // determine if there were any parsing errors |
| this.unitInfo.setIsStructureKnown(!this.hasSyntaxErrors); |
| } |
| /** |
| * @see ISourceElementRequestor |
| */ |
| public void exitConstructor(int declarationEnd) { |
| exitMember(declarationEnd); |
| } |
| /** |
| * @see ISourceElementRequestor |
| */ |
| public void exitField(int initializationStart, int declarationEnd, int declarationSourceEnd) { |
| SourceFieldElementInfo info = (SourceFieldElementInfo) this.infoStack.pop(); |
| info.setSourceRangeEnd(declarationSourceEnd); |
| |
| // remember initializer source if field is a constant |
| if (initializationStart != -1) { |
| int flags = info.flags; |
| Object typeInfo; |
| if (Flags.isStatic(flags) && Flags.isFinal(flags) |
| || ((typeInfo = this.infoStack.peek()) instanceof SourceTypeElementInfo |
| && (Flags.isInterface(((SourceTypeElementInfo)typeInfo).flags)))) { |
| 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; |
| } |
| } |
| } |
| this.handleStack.pop(); |
| } |
| /** |
| * @see ISourceElementRequestor |
| */ |
| public void exitInitializer(int declarationEnd) { |
| exitMember(declarationEnd); |
| } |
| /** |
| * common processing for classes and interfaces |
| */ |
| protected void exitMember(int declarationEnd) { |
| SourceRefElementInfo info = (SourceRefElementInfo) this.infoStack.pop(); |
| info.setSourceRangeEnd(declarationEnd); |
| this.handleStack.pop(); |
| } |
| /** |
| * @see ISourceElementRequestor |
| */ |
| public void exitMethod(int declarationEnd, int defaultValueStart, int defaultValueEnd) { |
| SourceMethodElementInfo info = (SourceMethodElementInfo) this.infoStack.pop(); |
| info.setSourceRangeEnd(declarationEnd); |
| |
| // remember default value of annotation method |
| if (info.isAnnotationMethod()) { |
| SourceAnnotationMethodInfo annotationMethodInfo = (SourceAnnotationMethodInfo) info; |
| annotationMethodInfo.defaultValueStart = defaultValueStart; |
| annotationMethodInfo.defaultValueEnd = defaultValueEnd; |
| } |
| this.handleStack.pop(); |
| } |
| /** |
| * @see ISourceElementRequestor |
| */ |
| public void exitType(int declarationEnd) { |
| |
| exitMember(declarationEnd); |
| } |
| /** |
| * Resolves duplicate handles by incrementing the occurrence count |
| * of the handle being created until there is no conflict. |
| */ |
| protected void resolveDuplicates(SourceRefElement handle) { |
| while (this.newElements.containsKey(handle)) { |
| handle.occurrenceCount++; |
| } |
| } |
| } |