/******************************************************************************* * Copyright (c) 2000, 2009 IBM Corporation and others. * 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: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jdt.internal.compiler; import java.util.HashMap; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.jdt.core.compiler.CategorizedProblem; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.AllocationExpression; import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.ArrayQualifiedTypeReference; import org.eclipse.jdt.internal.compiler.ast.ArrayTypeReference; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration; import org.eclipse.jdt.internal.compiler.ast.Expression; import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; import org.eclipse.jdt.internal.compiler.ast.FieldReference; import org.eclipse.jdt.internal.compiler.ast.ImportReference; import org.eclipse.jdt.internal.compiler.ast.JavadocAllocationExpression; import org.eclipse.jdt.internal.compiler.ast.JavadocFieldReference; import org.eclipse.jdt.internal.compiler.ast.JavadocMessageSend; import org.eclipse.jdt.internal.compiler.ast.JavadocQualifiedTypeReference; import org.eclipse.jdt.internal.compiler.ast.JavadocSingleTypeReference; import org.eclipse.jdt.internal.compiler.ast.MemberValuePair; import org.eclipse.jdt.internal.compiler.ast.MessageSend; import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.NameReference; import org.eclipse.jdt.internal.compiler.ast.ParameterizedQualifiedTypeReference; import org.eclipse.jdt.internal.compiler.ast.ParameterizedSingleTypeReference; import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference; import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference; import org.eclipse.jdt.internal.compiler.ast.SingleMemberAnnotation; import org.eclipse.jdt.internal.compiler.ast.SingleNameReference; import org.eclipse.jdt.internal.compiler.ast.SingleTypeReference; import org.eclipse.jdt.internal.compiler.ast.TypeReference; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; import org.eclipse.jdt.internal.compiler.impl.ReferenceContext; import org.eclipse.jdt.internal.compiler.lookup.Binding; import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; import org.eclipse.jdt.internal.compiler.problem.AbortCompilation; import org.eclipse.jdt.internal.compiler.problem.ProblemReporter; import org.eclipse.jdt.internal.compiler.util.HashtableOfObjectToInt; import org.eclipse.jdt.internal.core.util.CommentRecorderParser; import org.eclipse.jdt.internal.core.util.Messages; /** * A source element parser extracts structural and reference information from a piece of source. * * also see @ISourceElementRequestor * * The structural investigation includes: - the package statement - import statements - top-level * types: package member, member types (member types of member types...) - fields - methods * * If reference information is requested, then all source constructs are investigated and type, * field & method references are provided as well. * * Any (parsing) problem encountered is also provided. */ public class SourceElementParser extends CommentRecorderParser { ISourceElementRequestor requestor; boolean reportReferenceInfo; boolean reportLocalDeclarations; HashtableOfObjectToInt sourceEnds= new HashtableOfObjectToInt(); HashMap nodesToCategories= new HashMap(); // a map from ASTNode to char[][] boolean useSourceJavadocParser= true; SourceElementNotifier notifier; public SourceElementParser( final ISourceElementRequestor requestor, IProblemFactory problemFactory, CompilerOptions options, boolean reportLocalDeclarations, boolean optimizeStringLiterals) { this(requestor, problemFactory, options, reportLocalDeclarations, optimizeStringLiterals, true/* use SourceJavadocParser */); } public SourceElementParser( ISourceElementRequestor requestor, IProblemFactory problemFactory, CompilerOptions options, boolean reportLocalDeclarations, boolean optimizeStringLiterals, boolean useSourceJavadocParser) { super( new ProblemReporter( DefaultErrorHandlingPolicies.exitAfterAllProblems(), options, problemFactory), optimizeStringLiterals); this.reportLocalDeclarations= reportLocalDeclarations; // we want to notify all syntax error with the acceptProblem API // To do so, we define the record method of the ProblemReporter this.problemReporter= new ProblemReporter( DefaultErrorHandlingPolicies.exitAfterAllProblems(), options, problemFactory) { public void record(CategorizedProblem problem, CompilationResult unitResult, ReferenceContext context) { unitResult.record(problem, context); // TODO (jerome) clients are trapping problems either through factory or requestor... is result storing needed? SourceElementParser.this.requestor.acceptProblem(problem); } }; this.requestor= requestor; this.options= options; this.notifier= new SourceElementNotifier(this.requestor, reportLocalDeclarations); // set specific javadoc parser this.useSourceJavadocParser= useSourceJavadocParser; if (useSourceJavadocParser) { this.javadocParser= new SourceJavadocParser(this); } } private void acceptJavadocTypeReference(Expression expression) { if (expression instanceof JavadocSingleTypeReference) { JavadocSingleTypeReference singleRef= (JavadocSingleTypeReference)expression; this.requestor.acceptTypeReference(singleRef.token, singleRef.sourceStart); } else if (expression instanceof JavadocQualifiedTypeReference) { JavadocQualifiedTypeReference qualifiedRef= (JavadocQualifiedTypeReference)expression; this.requestor.acceptTypeReference(qualifiedRef.tokens, qualifiedRef.sourceStart, qualifiedRef.sourceEnd); } } public void addUnknownRef(NameReference nameRef) { // Note that: // - the only requestor interested in references is the SourceIndexerRequestor // - a name reference can become a type reference only during the cast case, it is then tagged later with the Binding.TYPE bit // However since the indexer doesn't make the distinction between name reference and type reference, there is no need // to report a type reference in the SourceElementParser. // This gained 3.7% in the indexing performance test. if (nameRef instanceof SingleNameReference) { this.requestor.acceptUnknownReference(((SingleNameReference)nameRef).token, nameRef.sourceStart); } else { //QualifiedNameReference this.requestor.acceptUnknownReference(((QualifiedNameReference)nameRef).tokens, nameRef.sourceStart, nameRef.sourceEnd); } } public void checkComment() { // discard obsolete comments while inside methods or fields initializer (see bug 74369) if (!(this.diet && this.dietInt == 0) && this.scanner.commentPtr >= 0) { flushCommentsDefinedPriorTo(this.endStatementPosition); } int lastComment= this.scanner.commentPtr; if (this.modifiersSourceStart >= 0) { // eliminate comments located after modifierSourceStart if positionned while (lastComment >= 0) { int commentSourceStart= this.scanner.commentStarts[lastComment]; if (commentSourceStart < 0) commentSourceStart= -commentSourceStart; if (commentSourceStart <= this.modifiersSourceStart) break; lastComment--; } } if (lastComment >= 0) { // consider all remaining leading comments to be part of current declaration this.modifiersSourceStart= this.scanner.commentStarts[0]; if (this.modifiersSourceStart < 0) this.modifiersSourceStart= -this.modifiersSourceStart; // check deprecation in last comment if javadoc (can be followed by non-javadoc comments which are simply ignored) while (lastComment >= 0 && this.scanner.commentStops[lastComment] < 0) lastComment--; // non javadoc comment have negative end positions if (lastComment >= 0 && this.javadocParser != null) { int commentEnd= this.scanner.commentStops[lastComment] - 1; //stop is one over // do not report problem before last parsed comment while recovering code... if (this.javadocParser.shouldReportProblems) { this.javadocParser.reportProblems= this.currentElement == null || commentEnd > this.lastJavadocEnd; } else { this.javadocParser.reportProblems= false; } if (this.javadocParser.checkDeprecation(lastComment)) { checkAndSetModifiers(ClassFileConstants.AccDeprecated); } this.javadoc= this.javadocParser.docComment; // null if check javadoc is not activated if (this.currentElement == null) this.lastJavadocEnd= commentEnd; } } if (this.reportReferenceInfo && this.javadocParser.checkDocComment && this.javadoc != null) { // Report reference info in javadoc comment @throws/@exception tags TypeReference[] thrownExceptions= this.javadoc.exceptionReferences; if (thrownExceptions != null) { for (int i= 0, max= thrownExceptions.length; i < max; i++) { TypeReference typeRef= thrownExceptions[i]; if (typeRef instanceof JavadocSingleTypeReference) { JavadocSingleTypeReference singleRef= (JavadocSingleTypeReference)typeRef; this.requestor.acceptTypeReference(singleRef.token, singleRef.sourceStart); } else if (typeRef instanceof JavadocQualifiedTypeReference) { JavadocQualifiedTypeReference qualifiedRef= (JavadocQualifiedTypeReference)typeRef; this.requestor.acceptTypeReference(qualifiedRef.tokens, qualifiedRef.sourceStart, qualifiedRef.sourceEnd); } } } // Report reference info in javadoc comment @see tags Expression[] references= this.javadoc.seeReferences; if (references != null) { for (int i= 0, max= references.length; i < max; i++) { Expression reference= references[i]; acceptJavadocTypeReference(reference); if (reference instanceof JavadocFieldReference) { JavadocFieldReference fieldRef= (JavadocFieldReference)reference; this.requestor.acceptFieldReference(fieldRef.token, fieldRef.sourceStart); if (fieldRef.receiver != null && !fieldRef.receiver.isThis()) { acceptJavadocTypeReference(fieldRef.receiver); } } else if (reference instanceof JavadocMessageSend) { JavadocMessageSend messageSend= (JavadocMessageSend)reference; int argCount= messageSend.arguments == null ? 0 : messageSend.arguments.length; this.requestor.acceptMethodReference(messageSend.selector, argCount, messageSend.sourceStart); this.requestor.acceptConstructorReference(messageSend.selector, argCount, messageSend.sourceStart); if (messageSend.receiver != null && !messageSend.receiver.isThis()) { acceptJavadocTypeReference(messageSend.receiver); } } else if (reference instanceof JavadocAllocationExpression) { JavadocAllocationExpression constructor= (JavadocAllocationExpression)reference; int argCount= constructor.arguments == null ? 0 : constructor.arguments.length; if (constructor.type != null) { char[][] compoundName= constructor.type.getParameterizedTypeName(); this.requestor.acceptConstructorReference(compoundName[compoundName.length - 1], argCount, constructor.sourceStart); if (!constructor.type.isThis()) { acceptJavadocTypeReference(constructor.type); } } } } } } } protected void classInstanceCreation(boolean alwaysQualified) { boolean previousFlag= this.reportReferenceInfo; this.reportReferenceInfo= false; // not to see the type reference reported in super call to getTypeReference(...) super.classInstanceCreation(alwaysQualified); this.reportReferenceInfo= previousFlag; if (this.reportReferenceInfo) { AllocationExpression alloc= (AllocationExpression)this.expressionStack[this.expressionPtr]; TypeReference typeRef= alloc.type; this.requestor.acceptConstructorReference( typeRef instanceof SingleTypeReference ? ((SingleTypeReference)typeRef).token : CharOperation.concatWith(alloc.type.getParameterizedTypeName(), '.'), alloc.arguments == null ? 0 : alloc.arguments.length, alloc.sourceStart); } } protected void consumeAnnotationAsModifier() { super.consumeAnnotationAsModifier(); Annotation annotation= (Annotation)this.expressionStack[this.expressionPtr]; if (this.reportReferenceInfo) { // accept annotation type reference this.requestor.acceptAnnotationTypeReference(annotation.type.getTypeName(), annotation.sourceStart, annotation.sourceEnd); } } protected void consumeClassInstanceCreationExpressionQualifiedWithTypeArguments() { boolean previousFlag= this.reportReferenceInfo; this.reportReferenceInfo= false; // not to see the type reference reported in super call to getTypeReference(...) super.consumeClassInstanceCreationExpressionQualifiedWithTypeArguments(); this.reportReferenceInfo= previousFlag; if (this.reportReferenceInfo) { AllocationExpression alloc= (AllocationExpression)this.expressionStack[this.expressionPtr]; TypeReference typeRef= alloc.type; this.requestor.acceptConstructorReference( typeRef instanceof SingleTypeReference ? ((SingleTypeReference)typeRef).token : CharOperation.concatWith(alloc.type.getParameterizedTypeName(), '.'), alloc.arguments == null ? 0 : alloc.arguments.length, alloc.sourceStart); } } protected void consumeAnnotationTypeDeclarationHeaderName() { int currentAstPtr= this.astPtr; super.consumeAnnotationTypeDeclarationHeaderName(); if (this.astPtr > currentAstPtr) // if ast node was pushed on the ast stack rememberCategories(); } protected void consumeAnnotationTypeDeclarationHeaderNameWithTypeParameters() { int currentAstPtr= this.astPtr; super.consumeAnnotationTypeDeclarationHeaderNameWithTypeParameters(); if (this.astPtr > currentAstPtr) // if ast node was pushed on the ast stack rememberCategories(); } protected void consumeClassHeaderName1() { int currentAstPtr= this.astPtr; super.consumeClassHeaderName1(); if (this.astPtr > currentAstPtr) // if ast node was pushed on the ast stack rememberCategories(); } protected void consumeClassInstanceCreationExpressionWithTypeArguments() { boolean previousFlag= this.reportReferenceInfo; this.reportReferenceInfo= false; // not to see the type reference reported in super call to getTypeReference(...) super.consumeClassInstanceCreationExpressionWithTypeArguments(); this.reportReferenceInfo= previousFlag; if (this.reportReferenceInfo) { AllocationExpression alloc= (AllocationExpression)this.expressionStack[this.expressionPtr]; TypeReference typeRef= alloc.type; this.requestor.acceptConstructorReference( typeRef instanceof SingleTypeReference ? ((SingleTypeReference)typeRef).token : CharOperation.concatWith(alloc.type.getParameterizedTypeName(), '.'), alloc.arguments == null ? 0 : alloc.arguments.length, alloc.sourceStart); } } protected void consumeConstructorHeaderName() { long selectorSourcePositions= this.identifierPositionStack[this.identifierPtr]; int selectorSourceEnd= (int)selectorSourcePositions; int currentAstPtr= this.astPtr; super.consumeConstructorHeaderName(); if (this.astPtr > currentAstPtr) { // if ast node was pushed on the ast stack this.sourceEnds.put(this.astStack[this.astPtr], selectorSourceEnd); rememberCategories(); } } protected void consumeConstructorHeaderNameWithTypeParameters() { long selectorSourcePositions= this.identifierPositionStack[this.identifierPtr]; int selectorSourceEnd= (int)selectorSourcePositions; int currentAstPtr= this.astPtr; super.consumeConstructorHeaderNameWithTypeParameters(); if (this.astPtr > currentAstPtr) { // if ast node was pushed on the ast stack this.sourceEnds.put(this.astStack[this.astPtr], selectorSourceEnd); rememberCategories(); } } protected void consumeEnumConstantWithClassBody() { super.consumeEnumConstantWithClassBody(); if ((this.currentToken == TokenNameCOMMA || this.currentToken == TokenNameSEMICOLON) && this.astStack[this.astPtr] instanceof FieldDeclaration) { this.sourceEnds.put(this.astStack[this.astPtr], this.scanner.currentPosition - 1); rememberCategories(); } } protected void consumeEnumConstantNoClassBody() { super.consumeEnumConstantNoClassBody(); if ((this.currentToken == TokenNameCOMMA || this.currentToken == TokenNameSEMICOLON) && this.astStack[this.astPtr] instanceof FieldDeclaration) { this.sourceEnds.put(this.astStack[this.astPtr], this.scanner.currentPosition - 1); rememberCategories(); } } protected void consumeEnumHeaderName() { int currentAstPtr= this.astPtr; super.consumeEnumHeaderName(); if (this.astPtr > currentAstPtr) // if ast node was pushed on the ast stack rememberCategories(); } protected void consumeEnumHeaderNameWithTypeParameters() { int currentAstPtr= this.astPtr; super.consumeEnumHeaderNameWithTypeParameters(); if (this.astPtr > currentAstPtr) // if ast node was pushed on the ast stack rememberCategories(); } protected void consumeExitVariableWithInitialization() { // ExitVariableWithInitialization ::= $empty // the scanner is located after the comma or the semi-colon. // we want to include the comma or the semi-colon super.consumeExitVariableWithInitialization(); if ((this.currentToken == TokenNameCOMMA || this.currentToken == TokenNameSEMICOLON) && this.astStack[this.astPtr] instanceof FieldDeclaration) { this.sourceEnds.put(this.astStack[this.astPtr], this.scanner.currentPosition - 1); rememberCategories(); } } protected void consumeExitVariableWithoutInitialization() { // ExitVariableWithoutInitialization ::= $empty // do nothing by default super.consumeExitVariableWithoutInitialization(); if ((this.currentToken == TokenNameCOMMA || this.currentToken == TokenNameSEMICOLON) && this.astStack[this.astPtr] instanceof FieldDeclaration) { this.sourceEnds.put(this.astStack[this.astPtr], this.scanner.currentPosition - 1); rememberCategories(); } } /* * * INTERNAL USE-ONLY */ protected void consumeFieldAccess(boolean isSuperAccess) { // FieldAccess ::= Primary '.' 'Identifier' // FieldAccess ::= 'super' '.' 'Identifier' super.consumeFieldAccess(isSuperAccess); FieldReference fr= (FieldReference)this.expressionStack[this.expressionPtr]; if (this.reportReferenceInfo) { this.requestor.acceptFieldReference(fr.token, fr.sourceStart); } } protected void consumeFormalParameter(boolean isVarArgs) { super.consumeFormalParameter(isVarArgs); // Flush comments prior to this formal parameter so the declarationSourceStart of the following parameter // is correctly set (see bug 80904) // Note that this could be done in the Parser itself, but this would slow down all parsers, when they don't need // the declarationSourceStart to be set flushCommentsDefinedPriorTo(this.scanner.currentPosition); } protected void consumeInterfaceHeaderName1() { int currentAstPtr= this.astPtr; super.consumeInterfaceHeaderName1(); if (this.astPtr > currentAstPtr) // if ast node was pushed on the ast stack rememberCategories(); } protected void consumeMemberValuePair() { super.consumeMemberValuePair(); MemberValuePair memberValuepair= (MemberValuePair)this.astStack[this.astPtr]; if (this.reportReferenceInfo) { this.requestor.acceptMethodReference(memberValuepair.name, 0, memberValuepair.sourceStart); } } protected void consumeMarkerAnnotation() { super.consumeMarkerAnnotation(); Annotation annotation= (Annotation)this.expressionStack[this.expressionPtr]; if (this.reportReferenceInfo) { // accept annotation type reference this.requestor.acceptAnnotationTypeReference(annotation.type.getTypeName(), annotation.sourceStart, annotation.sourceEnd); } } protected void consumeMethodHeaderName(boolean isAnnotationMethod) { long selectorSourcePositions= this.identifierPositionStack[this.identifierPtr]; int selectorSourceEnd= (int)selectorSourcePositions; int currentAstPtr= this.astPtr; super.consumeMethodHeaderName(isAnnotationMethod); if (this.astPtr > currentAstPtr) { // if ast node was pushed on the ast stack this.sourceEnds.put(this.astStack[this.astPtr], selectorSourceEnd); rememberCategories(); } } protected void consumeMethodHeaderNameWithTypeParameters(boolean isAnnotationMethod) { long selectorSourcePositions= this.identifierPositionStack[this.identifierPtr]; int selectorSourceEnd= (int)selectorSourcePositions; int currentAstPtr= this.astPtr; super.consumeMethodHeaderNameWithTypeParameters(isAnnotationMethod); if (this.astPtr > currentAstPtr) // if ast node was pushed on the ast stack this.sourceEnds.put(this.astStack[this.astPtr], selectorSourceEnd); rememberCategories(); } /* * * INTERNAL USE-ONLY */ protected void consumeMethodInvocationName() { // MethodInvocation ::= Name '(' ArgumentListopt ')' super.consumeMethodInvocationName(); // when the name is only an identifier...we have a message send to "this" (implicit) MessageSend messageSend= (MessageSend)this.expressionStack[this.expressionPtr]; Expression[] args= messageSend.arguments; if (this.reportReferenceInfo) { this.requestor.acceptMethodReference( messageSend.selector, args == null ? 0 : args.length, (int)(messageSend.nameSourcePosition >>> 32)); } } protected void consumeMethodInvocationNameWithTypeArguments() { // MethodInvocation ::= Name '.' TypeArguments 'Identifier' '(' ArgumentListopt ')' super.consumeMethodInvocationNameWithTypeArguments(); // when the name is only an identifier...we have a message send to "this" (implicit) MessageSend messageSend= (MessageSend)this.expressionStack[this.expressionPtr]; Expression[] args= messageSend.arguments; if (this.reportReferenceInfo) { this.requestor.acceptMethodReference( messageSend.selector, args == null ? 0 : args.length, (int)(messageSend.nameSourcePosition >>> 32)); } } /* * * INTERNAL USE-ONLY */ protected void consumeMethodInvocationPrimary() { super.consumeMethodInvocationPrimary(); MessageSend messageSend= (MessageSend)this.expressionStack[this.expressionPtr]; Expression[] args= messageSend.arguments; if (this.reportReferenceInfo) { this.requestor.acceptMethodReference( messageSend.selector, args == null ? 0 : args.length, (int)(messageSend.nameSourcePosition >>> 32)); } } /* * * INTERNAL USE-ONLY */ protected void consumeMethodInvocationPrimaryWithTypeArguments() { super.consumeMethodInvocationPrimaryWithTypeArguments(); MessageSend messageSend= (MessageSend)this.expressionStack[this.expressionPtr]; Expression[] args= messageSend.arguments; if (this.reportReferenceInfo) { this.requestor.acceptMethodReference( messageSend.selector, args == null ? 0 : args.length, (int)(messageSend.nameSourcePosition >>> 32)); } } /* * * INTERNAL USE-ONLY */ protected void consumeMethodInvocationSuper() { // MethodInvocation ::= 'super' '.' 'Identifier' '(' ArgumentListopt ')' super.consumeMethodInvocationSuper(); MessageSend messageSend= (MessageSend)this.expressionStack[this.expressionPtr]; Expression[] args= messageSend.arguments; if (this.reportReferenceInfo) { this.requestor.acceptMethodReference( messageSend.selector, args == null ? 0 : args.length, (int)(messageSend.nameSourcePosition >>> 32)); } } protected void consumeMethodInvocationSuperWithTypeArguments() { // MethodInvocation ::= 'super' '.' TypeArguments 'Identifier' '(' ArgumentListopt ')' super.consumeMethodInvocationSuperWithTypeArguments(); MessageSend messageSend= (MessageSend)this.expressionStack[this.expressionPtr]; Expression[] args= messageSend.arguments; if (this.reportReferenceInfo) { this.requestor.acceptMethodReference( messageSend.selector, args == null ? 0 : args.length, (int)(messageSend.nameSourcePosition >>> 32)); } } protected void consumeNormalAnnotation() { super.consumeNormalAnnotation(); Annotation annotation= (Annotation)this.expressionStack[this.expressionPtr]; if (this.reportReferenceInfo) { // accept annotation type reference this.requestor.acceptAnnotationTypeReference(annotation.type.getTypeName(), annotation.sourceStart, annotation.sourceEnd); } } protected void consumeSingleMemberAnnotation() { super.consumeSingleMemberAnnotation(); SingleMemberAnnotation member= (SingleMemberAnnotation)this.expressionStack[this.expressionPtr]; if (this.reportReferenceInfo) { this.requestor.acceptMethodReference(TypeConstants.VALUE, 0, member.sourceStart); } } protected void consumeSingleStaticImportDeclarationName() { // SingleTypeImportDeclarationName ::= 'import' 'static' Name ImportReference impt; int length; char[][] tokens= new char[length= this.identifierLengthStack[this.identifierLengthPtr--]][]; this.identifierPtr-= length; long[] positions= new long[length]; System.arraycopy(this.identifierStack, this.identifierPtr + 1, tokens, 0, length); System.arraycopy(this.identifierPositionStack, this.identifierPtr + 1, positions, 0, length); pushOnAstStack(impt= newImportReference(tokens, positions, false, ClassFileConstants.AccStatic)); this.modifiers= ClassFileConstants.AccDefault; this.modifiersSourceStart= -1; // <-- see comment into modifiersFlag(int) if (this.currentToken == TokenNameSEMICOLON) { impt.declarationSourceEnd= this.scanner.currentPosition - 1; } else { impt.declarationSourceEnd= impt.sourceEnd; } impt.declarationEnd= impt.declarationSourceEnd; //this.endPosition is just before the ; impt.declarationSourceStart= this.intStack[this.intPtr--]; if (!this.statementRecoveryActivated && this.options.sourceLevel < ClassFileConstants.JDK1_5 && this.lastErrorEndPositionBeforeRecovery < this.scanner.currentPosition) { impt.modifiers= ClassFileConstants.AccDefault; // convert the static import reference to a non-static importe reference problemReporter().invalidUsageOfStaticImports(impt); } // recovery if (this.currentElement != null) { this.lastCheckPoint= impt.declarationSourceEnd + 1; this.currentElement= this.currentElement.add(impt, 0); this.lastIgnoredToken= -1; this.restartRecovery= true; // used to avoid branching back into the regular automaton } if (this.reportReferenceInfo) { // Name for static import is TypeName '.' Identifier // => accept unknown ref on identifier int tokensLength= impt.tokens.length - 1; int start= (int)(impt.sourcePositions[tokensLength] >>> 32); char[] last= impt.tokens[tokensLength]; // accept all possible kind for last name, index users will have to select the right one... // see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=86901 this.requestor.acceptFieldReference(last, start); this.requestor.acceptMethodReference(last, 0, start); this.requestor.acceptTypeReference(last, start); // accept type name if (tokensLength > 0) { char[][] compoundName= new char[tokensLength][]; System.arraycopy(impt.tokens, 0, compoundName, 0, tokensLength); int end= (int)impt.sourcePositions[tokensLength - 1]; this.requestor.acceptTypeReference(compoundName, impt.sourceStart, end); } } } protected void consumeSingleTypeImportDeclarationName() { // SingleTypeImportDeclarationName ::= 'import' Name /* push an ImportRef build from the last name stored in the identifier stack. */ ImportReference impt; int length; char[][] tokens= new char[length= this.identifierLengthStack[this.identifierLengthPtr--]][]; this.identifierPtr-= length; long[] positions= new long[length]; System.arraycopy(this.identifierStack, this.identifierPtr + 1, tokens, 0, length); System.arraycopy(this.identifierPositionStack, this.identifierPtr + 1, positions, 0, length); pushOnAstStack(impt= newImportReference(tokens, positions, false, ClassFileConstants.AccDefault)); if (this.currentToken == TokenNameSEMICOLON) { impt.declarationSourceEnd= this.scanner.currentPosition - 1; } else { impt.declarationSourceEnd= impt.sourceEnd; } impt.declarationEnd= impt.declarationSourceEnd; //this.endPosition is just before the ; impt.declarationSourceStart= this.intStack[this.intPtr--]; // recovery if (this.currentElement != null) { this.lastCheckPoint= impt.declarationSourceEnd + 1; this.currentElement= this.currentElement.add(impt, 0); this.lastIgnoredToken= -1; this.restartRecovery= true; // used to avoid branching back into the regular automaton } if (this.reportReferenceInfo) { this.requestor.acceptTypeReference(impt.tokens, impt.sourceStart, impt.sourceEnd); } } protected void consumeStaticImportOnDemandDeclarationName() { // TypeImportOnDemandDeclarationName ::= 'import' 'static' Name '.' '*' /* push an ImportRef build from the last name stored in the identifier stack. */ ImportReference impt; int length; char[][] tokens= new char[length= this.identifierLengthStack[this.identifierLengthPtr--]][]; this.identifierPtr-= length; long[] positions= new long[length]; System.arraycopy(this.identifierStack, this.identifierPtr + 1, tokens, 0, length); System.arraycopy(this.identifierPositionStack, this.identifierPtr + 1, positions, 0, length); pushOnAstStack(impt= new ImportReference(tokens, positions, true, ClassFileConstants.AccStatic)); this.modifiers= ClassFileConstants.AccDefault; this.modifiersSourceStart= -1; // <-- see comment into modifiersFlag(int) if (this.currentToken == TokenNameSEMICOLON) { impt.declarationSourceEnd= this.scanner.currentPosition - 1; } else { impt.declarationSourceEnd= impt.sourceEnd; } impt.declarationEnd= impt.declarationSourceEnd; //this.endPosition is just before the ; impt.declarationSourceStart= this.intStack[this.intPtr--]; if (!this.statementRecoveryActivated && this.options.sourceLevel < ClassFileConstants.JDK1_5 && this.lastErrorEndPositionBeforeRecovery < this.scanner.currentPosition) { impt.modifiers= ClassFileConstants.AccDefault; // convert the static import reference to a non-static importe reference problemReporter().invalidUsageOfStaticImports(impt); } // recovery if (this.currentElement != null) { this.lastCheckPoint= impt.declarationSourceEnd + 1; this.currentElement= this.currentElement.add(impt, 0); this.lastIgnoredToken= -1; this.restartRecovery= true; // used to avoid branching back into the regular automaton } if (this.reportReferenceInfo) { this.requestor.acceptTypeReference(impt.tokens, impt.sourceStart, impt.sourceEnd); } } protected void consumeTypeImportOnDemandDeclarationName() { // TypeImportOnDemandDeclarationName ::= 'import' Name '.' '*' /* push an ImportRef build from the last name stored in the identifier stack. */ ImportReference impt; int length; char[][] tokens= new char[length= this.identifierLengthStack[this.identifierLengthPtr--]][]; this.identifierPtr-= length; long[] positions= new long[length]; System.arraycopy(this.identifierStack, this.identifierPtr + 1, tokens, 0, length); System.arraycopy(this.identifierPositionStack, this.identifierPtr + 1, positions, 0, length); pushOnAstStack(impt= new ImportReference(tokens, positions, true, ClassFileConstants.AccDefault)); if (this.currentToken == TokenNameSEMICOLON) { impt.declarationSourceEnd= this.scanner.currentPosition - 1; } else { impt.declarationSourceEnd= impt.sourceEnd; } impt.declarationEnd= impt.declarationSourceEnd; //this.endPosition is just before the ; impt.declarationSourceStart= this.intStack[this.intPtr--]; // recovery if (this.currentElement != null) { this.lastCheckPoint= impt.declarationSourceEnd + 1; this.currentElement= this.currentElement.add(impt, 0); this.lastIgnoredToken= -1; this.restartRecovery= true; // used to avoid branching back into the regular automaton } if (this.reportReferenceInfo) { this.requestor.acceptUnknownReference(impt.tokens, impt.sourceStart, impt.sourceEnd); } } public MethodDeclaration convertToMethodDeclaration(ConstructorDeclaration c, CompilationResult compilationResult) { MethodDeclaration methodDeclaration= super.convertToMethodDeclaration(c, compilationResult); int selectorSourceEnd= this.sourceEnds.removeKey(c); if (selectorSourceEnd != -1) this.sourceEnds.put(methodDeclaration, selectorSourceEnd); char[][] categories= (char[][])this.nodesToCategories.remove(c); if (categories != null) this.nodesToCategories.put(methodDeclaration, categories); return methodDeclaration; } protected CompilationUnitDeclaration endParse(int act) { if (this.scanner.recordLineSeparator) { this.requestor.acceptLineSeparatorPositions(this.scanner.getLineEnds()); } if (this.compilationUnit != null) { CompilationUnitDeclaration result= super.endParse(act); return result; } else { return null; } } public TypeReference getTypeReference(int dim) { /* build a Reference on a variable that may be qualified or not * This variable is a type reference and dim will be its dimensions */ int length= this.identifierLengthStack[this.identifierLengthPtr--]; if (length < 0) { //flag for precompiled type reference on base types TypeReference ref= TypeReference.baseTypeReference(-length, dim); ref.sourceStart= this.intStack[this.intPtr--]; if (dim == 0) { ref.sourceEnd= this.intStack[this.intPtr--]; } else { this.intPtr--; // no need to use this position as it is an array ref.sourceEnd= this.endPosition; } if (this.reportReferenceInfo) { this.requestor.acceptTypeReference(ref.getParameterizedTypeName(), ref.sourceStart, ref.sourceEnd); } return ref; } else { int numberOfIdentifiers= this.genericsIdentifiersLengthStack[this.genericsIdentifiersLengthPtr--]; if (length != numberOfIdentifiers || this.genericsLengthStack[this.genericsLengthPtr] != 0) { // generic type TypeReference ref= getTypeReferenceForGenericType(dim, length, numberOfIdentifiers); if (this.reportReferenceInfo) { if (length == 1 && numberOfIdentifiers == 1) { ParameterizedSingleTypeReference parameterizedSingleTypeReference= (ParameterizedSingleTypeReference)ref; this.requestor.acceptTypeReference(parameterizedSingleTypeReference.token, parameterizedSingleTypeReference.sourceStart); } else { ParameterizedQualifiedTypeReference parameterizedQualifiedTypeReference= (ParameterizedQualifiedTypeReference)ref; this.requestor.acceptTypeReference(parameterizedQualifiedTypeReference.tokens, parameterizedQualifiedTypeReference.sourceStart, parameterizedQualifiedTypeReference.sourceEnd); } } return ref; } else if (length == 1) { // single variable reference this.genericsLengthPtr--; // pop the 0 if (dim == 0) { SingleTypeReference ref= new SingleTypeReference( this.identifierStack[this.identifierPtr], this.identifierPositionStack[this.identifierPtr--]); if (this.reportReferenceInfo) { this.requestor.acceptTypeReference(ref.token, ref.sourceStart); } return ref; } else { ArrayTypeReference ref= new ArrayTypeReference( this.identifierStack[this.identifierPtr], dim, this.identifierPositionStack[this.identifierPtr--]); ref.sourceEnd= this.endPosition; if (this.reportReferenceInfo) { this.requestor.acceptTypeReference(ref.token, ref.sourceStart); } return ref; } } else {//Qualified variable reference this.genericsLengthPtr--; char[][] tokens= new char[length][]; this.identifierPtr-= length; long[] positions= new long[length]; System.arraycopy(this.identifierStack, this.identifierPtr + 1, tokens, 0, length); System.arraycopy( this.identifierPositionStack, this.identifierPtr + 1, positions, 0, length); if (dim == 0) { QualifiedTypeReference ref= new QualifiedTypeReference(tokens, positions); if (this.reportReferenceInfo) { this.requestor.acceptTypeReference(ref.tokens, ref.sourceStart, ref.sourceEnd); } return ref; } else { ArrayQualifiedTypeReference ref= new ArrayQualifiedTypeReference(tokens, dim, positions); ref.sourceEnd= this.endPosition; if (this.reportReferenceInfo) { this.requestor.acceptTypeReference(ref.tokens, ref.sourceStart, ref.sourceEnd); } return ref; } } } } public NameReference getUnspecifiedReference() { /* build a (unspecified) NameReference which may be qualified*/ int length; if ((length= this.identifierLengthStack[this.identifierLengthPtr--]) == 1) { // single variable reference SingleNameReference ref= newSingleNameReference( this.identifierStack[this.identifierPtr], this.identifierPositionStack[this.identifierPtr--]); if (this.reportReferenceInfo) { addUnknownRef(ref); } return ref; } else { //Qualified variable reference char[][] tokens= new char[length][]; this.identifierPtr-= length; System.arraycopy(this.identifierStack, this.identifierPtr + 1, tokens, 0, length); long[] positions= new long[length]; System.arraycopy(this.identifierPositionStack, this.identifierPtr + 1, positions, 0, length); QualifiedNameReference ref= newQualifiedNameReference( tokens, positions, (int)(this.identifierPositionStack[this.identifierPtr + 1] >> 32), // sourceStart (int)this.identifierPositionStack[this.identifierPtr + length]); // sourceEnd if (this.reportReferenceInfo) { addUnknownRef(ref); } return ref; } } public NameReference getUnspecifiedReferenceOptimized() { /* build a (unspecified) NameReference which may be qualified The optimization occurs for qualified reference while we are certain in this case the last item of the qualified name is a field access. This optimization is IMPORTANT while it results that when a NameReference is build, the type checker should always look for that it is not a type reference */ int length; if ((length= this.identifierLengthStack[this.identifierLengthPtr--]) == 1) { // single variable reference SingleNameReference ref= newSingleNameReference( this.identifierStack[this.identifierPtr], this.identifierPositionStack[this.identifierPtr--]); ref.bits&= ~ASTNode.RestrictiveFlagMASK; ref.bits|= Binding.LOCAL | Binding.FIELD; if (this.reportReferenceInfo) { addUnknownRef(ref); } return ref; } //Qualified-variable-reference //In fact it is variable-reference DOT field-ref , but it would result in a type //conflict tha can be only reduce by making a superclass (or inetrface ) between //nameReference and FiledReference or putting FieldReference under NameReference //or else..........This optimisation is not really relevant so just leave as it is char[][] tokens= new char[length][]; this.identifierPtr-= length; System.arraycopy(this.identifierStack, this.identifierPtr + 1, tokens, 0, length); long[] positions= new long[length]; System.arraycopy(this.identifierPositionStack, this.identifierPtr + 1, positions, 0, length); QualifiedNameReference ref= newQualifiedNameReference( tokens, positions, (int)(this.identifierPositionStack[this.identifierPtr + 1] >> 32), // sourceStart (int)this.identifierPositionStack[this.identifierPtr + length]); // sourceEnd ref.bits&= ~ASTNode.RestrictiveFlagMASK; ref.bits|= Binding.LOCAL | Binding.FIELD; if (this.reportReferenceInfo) { addUnknownRef(ref); } return ref; } protected ImportReference newImportReference(char[][] tokens, long[] positions, boolean onDemand, int mod) { return new ImportReference(tokens, positions, onDemand, mod); } protected QualifiedNameReference newQualifiedNameReference(char[][] tokens, long[] positions, int sourceStart, int sourceEnd) { return new QualifiedNameReference(tokens, positions, sourceStart, sourceEnd); } protected SingleNameReference newSingleNameReference(char[] source, long positions) { return new SingleNameReference(source, positions); } public CompilationUnitDeclaration parseCompilationUnit( ICompilationUnit unit, boolean fullParse, IProgressMonitor pm) { boolean old= this.diet; CompilationUnitDeclaration parsedUnit= null; try { this.diet= true; this.reportReferenceInfo= fullParse; CompilationResult compilationUnitResult= new CompilationResult(unit, 0, 0, this.options.maxProblemsPerUnit); parsedUnit= parse(unit, compilationUnitResult); if (pm != null && pm.isCanceled()) throw new OperationCanceledException(Messages.operation_cancelled); if (this.scanner.recordLineSeparator) { this.requestor.acceptLineSeparatorPositions(compilationUnitResult.getLineSeparatorPositions()); } int initialStart= this.scanner.initialPosition; int initialEnd= this.scanner.eofPosition; if (this.reportLocalDeclarations || fullParse) { this.diet= false; getMethodBodies(parsedUnit); } this.scanner.resetTo(initialStart, initialEnd); this.notifier.notifySourceElementRequestor( parsedUnit, this.scanner.initialPosition, this.scanner.eofPosition, this.reportReferenceInfo, this.sourceEnds, this.nodesToCategories); return parsedUnit; } catch (AbortCompilation e) { // ignore this exception } finally { this.diet= old; reset(); } return parsedUnit; } private void rememberCategories() { if (this.useSourceJavadocParser) { SourceJavadocParser sourceJavadocParser= (SourceJavadocParser)this.javadocParser; char[][] categories= sourceJavadocParser.categories; if (categories.length > 0) { this.nodesToCategories.put(this.astStack[this.astPtr], categories); sourceJavadocParser.categories= CharOperation.NO_CHAR_CHAR; } } } private void reset() { this.sourceEnds= new HashtableOfObjectToInt(); this.nodesToCategories= new HashMap(); } public void setRequestor(ISourceElementRequestor requestor) { this.requestor= requestor; this.notifier.requestor= requestor; } }