/*******************************************************************************
 * Copyright (c) 2000, 2016 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *     Jesper S Moller - Bug 392671
 *          NPE with a method with explicit this and a following incomplete parameter
 *******************************************************************************/
package org.eclipse.jdt.internal.compiler.parser;

import java.util.HashSet;
import java.util.Set;

import org.eclipse.jdt.core.compiler.*;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.Block;
import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ExplicitConstructorCall;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
import org.eclipse.jdt.internal.compiler.ast.MemberValuePair;
import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Statement;
import org.eclipse.jdt.internal.compiler.ast.SuperReference;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeParameter;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.util.Util;

/**
 * Internal method structure for parsing recovery
 */

public class RecoveredMethod extends RecoveredElement implements TerminalTokens {

	public AbstractMethodDeclaration methodDeclaration;

	public RecoveredAnnotation[] annotations;
	public int annotationCount;

	public int modifiers;
	public int modifiersStart;

	public RecoveredType[] localTypes;
	public int localTypeCount;

	public RecoveredBlock methodBody;
	public boolean discardBody = true;

	int pendingModifiers;
	int pendingModifersSourceStart = -1;
	RecoveredAnnotation[] pendingAnnotations;
	int pendingAnnotationCount;

public RecoveredMethod(AbstractMethodDeclaration methodDeclaration, RecoveredElement parent, int bracketBalance, Parser parser){
	super(parent, bracketBalance, parser);
	this.methodDeclaration = methodDeclaration;
	this.foundOpeningBrace = !bodyStartsAtHeaderEnd();
	if(this.foundOpeningBrace) {
		this.bracketBalance++;
	}
}
/*
 * Record a nested block declaration
 */
@Override
public RecoveredElement add(Block nestedBlockDeclaration, int bracketBalanceValue) {
	return this.add(nestedBlockDeclaration, bracketBalanceValue, false);
}
public RecoveredElement add(Block nestedBlockDeclaration, int bracketBalanceValue, boolean isArgument) {
	/* default behavior is to delegate recording to parent if any,
	do not consider elements passed the known end (if set)
	it must be belonging to an enclosing element
	*/
	if (this.methodDeclaration.declarationSourceEnd > 0
		&& nestedBlockDeclaration.sourceStart
			> this.methodDeclaration.declarationSourceEnd){
				resetPendingModifiers();
				if (this.parent == null){
					return this; // ignore
				} else {
					return this.parent.add(nestedBlockDeclaration, bracketBalanceValue);
				}
	}
	/* consider that if the opening brace was not found, it is there */
	if (!this.foundOpeningBrace && !isArgument){
		this.foundOpeningBrace = true;
		this.bracketBalance++;
	}

	if (this.methodBody != null) {
		this.methodBody.addBlockStatement(new RecoveredBlock(nestedBlockDeclaration, this, bracketBalanceValue));
	} else {
		this.methodBody = new RecoveredBlock(nestedBlockDeclaration, this, bracketBalanceValue);
	}
	if (nestedBlockDeclaration.sourceEnd == 0) return this.methodBody;
	return this;
}
/*
 * Record a field declaration
 */
@Override
public RecoveredElement add(FieldDeclaration fieldDeclaration, int bracketBalanceValue) {
	resetPendingModifiers();

	/* local variables inside method can only be final and non void */
	char[][] fieldTypeName;
	if ((fieldDeclaration.modifiers & ~ClassFileConstants.AccFinal) != 0 // local var can only be final
		|| (fieldDeclaration.type == null) // initializer
		|| ((fieldTypeName = fieldDeclaration.type.getTypeName()).length == 1 // non void
			&& CharOperation.equals(fieldTypeName[0], TypeBinding.VOID.sourceName()))){
		if (this.parent == null){
			return this; // ignore
		} else {
			this.updateSourceEndIfNecessary(previousAvailableLineEnd(fieldDeclaration.declarationSourceStart - 1));
			return this.parent.add(fieldDeclaration, bracketBalanceValue);
		}
	}
	/* default behavior is to delegate recording to parent if any,
	do not consider elements passed the known end (if set)
	it must be belonging to an enclosing element
	*/
	if (this.methodDeclaration.declarationSourceEnd > 0
		&& fieldDeclaration.declarationSourceStart
			> this.methodDeclaration.declarationSourceEnd){
		if (this.parent == null){
			return this; // ignore
		} else {
			return this.parent.add(fieldDeclaration, bracketBalanceValue);
		}
	}
	/* consider that if the opening brace was not found, it is there */
	if (!this.foundOpeningBrace){
		this.foundOpeningBrace = true;
		this.bracketBalance++;
	}
	// still inside method, treat as local variable
	return this; // ignore
}
/*
 * Record a local declaration - regular method should have been created a block body
 */
@Override
public RecoveredElement add(LocalDeclaration localDeclaration, int bracketBalanceValue) {
	resetPendingModifiers();

	/* local variables inside method can only be final and non void */
/*
	char[][] localTypeName;
	if ((localDeclaration.modifiers & ~AccFinal) != 0 // local var can only be final
		|| (localDeclaration.type == null) // initializer
		|| ((localTypeName = localDeclaration.type.getTypeName()).length == 1 // non void
			&& CharOperation.equals(localTypeName[0], VoidBinding.sourceName()))){

		if (this.parent == null){
			return this; // ignore
		} else {
			this.updateSourceEndIfNecessary(this.previousAvailableLineEnd(localDeclaration.declarationSourceStart - 1));
			return this.parent.add(localDeclaration, bracketBalance);
		}
	}
*/
	/* do not consider a type starting passed the type end (if set)
		it must be belonging to an enclosing type */
	if (this.methodDeclaration.declarationSourceEnd != 0
		&& localDeclaration.declarationSourceStart > this.methodDeclaration.declarationSourceEnd){

		if (this.parent == null) {
			return this; // ignore
		} else {
			return this.parent.add(localDeclaration, bracketBalanceValue);
		}
	}
	if (this.methodBody == null){
		Block block = new Block(0);
		block.sourceStart = this.methodDeclaration.bodyStart;
		RecoveredElement currentBlock = this.add(block, 1, localDeclaration.isArgument());
		if (this.bracketBalance > 0){
			for (int i = 0; i < this.bracketBalance - 1; i++){
				currentBlock = currentBlock.add(new Block(0), 1);
			}
			this.bracketBalance = 1;
		}
		return currentBlock.add(localDeclaration, bracketBalanceValue);
	}
	return this.methodBody.add(localDeclaration, bracketBalanceValue, true);
}
/*
 * Record a statement - regular method should have been created a block body
 */
@Override
public RecoveredElement add(Statement statement, int bracketBalanceValue) {
	resetPendingModifiers();

	/* do not consider a type starting passed the type end (if set)
		it must be belonging to an enclosing type */
	if (this.methodDeclaration.declarationSourceEnd != 0
		&& statement.sourceStart > this.methodDeclaration.declarationSourceEnd){

		if (this.parent == null) {
			return this; // ignore
		} else {
			return this.parent.add(statement, bracketBalanceValue);
		}
	}
	if (this.methodBody == null){
		Block block = new Block(0);
		block.sourceStart = this.methodDeclaration.bodyStart;
		RecoveredElement currentBlock = this.add(block, 1);
		if (this.bracketBalance > 0){
			for (int i = 0; i < this.bracketBalance - 1; i++){
				currentBlock = currentBlock.add(new Block(0), 1);
			}
			this.bracketBalance = 1;
		}
		return currentBlock.add(statement, bracketBalanceValue);
	}
	return this.methodBody.add(statement, bracketBalanceValue, true);
}
@Override
public RecoveredElement add(TypeDeclaration typeDeclaration, int bracketBalanceValue) {

	/* do not consider a type starting passed the type end (if set)
		it must be belonging to an enclosing type */
	if (this.methodDeclaration.declarationSourceEnd != 0
		&& typeDeclaration.declarationSourceStart > this.methodDeclaration.declarationSourceEnd){

		if (this.parent == null) {
			return this; // ignore
		}
		return this.parent.add(typeDeclaration, bracketBalanceValue);
	}
	if ((typeDeclaration.bits & ASTNode.IsLocalType) != 0 || parser().methodRecoveryActivated || parser().statementRecoveryActivated){
		if (this.methodBody == null){
			Block block = new Block(0);
			block.sourceStart = this.methodDeclaration.bodyStart;
			this.add(block, 1);
		}
		this.methodBody.attachPendingModifiers(
				this.pendingAnnotations,
				this.pendingAnnotationCount,
				this.pendingModifiers,
				this.pendingModifersSourceStart);
		resetPendingModifiers();
		return this.methodBody.add(typeDeclaration, bracketBalanceValue, true);
	}
	switch (TypeDeclaration.kind(typeDeclaration.modifiers)) {
		case TypeDeclaration.INTERFACE_DECL :
		case TypeDeclaration.ANNOTATION_TYPE_DECL :
			resetPendingModifiers();
			this.updateSourceEndIfNecessary(previousAvailableLineEnd(typeDeclaration.declarationSourceStart - 1));
			if (this.parent == null) {
				return this; // ignore
			}
			// close the constructor
			return this.parent.add(typeDeclaration, bracketBalanceValue);
	}
	if (this.localTypes == null) {
		this.localTypes = new RecoveredType[5];
		this.localTypeCount = 0;
	} else {
		if (this.localTypeCount == this.localTypes.length) {
			System.arraycopy(
				this.localTypes,
				0,
				(this.localTypes = new RecoveredType[2 * this.localTypeCount]),
				0,
				this.localTypeCount);
		}
	}
	RecoveredType element = new RecoveredType(typeDeclaration, this, bracketBalanceValue);
	this.localTypes[this.localTypeCount++] = element;

	if(this.pendingAnnotationCount > 0) {
		element.attach(
				this.pendingAnnotations,
				this.pendingAnnotationCount,
				this.pendingModifiers,
				this.pendingModifersSourceStart);
	}
	resetPendingModifiers();

	/* consider that if the opening brace was not found, it is there */
	if (!this.foundOpeningBrace){
		this.foundOpeningBrace = true;
		this.bracketBalance++;
	}
	return element;
}
public boolean bodyStartsAtHeaderEnd(){
	return this.methodDeclaration.bodyStart == this.methodDeclaration.sourceEnd+1;
}
/*
 * Answer the associated parsed structure
 */
@Override
public ASTNode parseTree(){
	return this.methodDeclaration;
}
@Override
public void resetPendingModifiers() {
	this.pendingAnnotations = null;
	this.pendingAnnotationCount = 0;
	this.pendingModifiers = 0;
	this.pendingModifersSourceStart = -1;
}
/*
 * Answer the very source end of the corresponding parse node
 */
@Override
public int sourceEnd(){
	return this.methodDeclaration.declarationSourceEnd;
}
@Override
public String toString(int tab) {
	StringBuffer result = new StringBuffer(tabString(tab));
	result.append("Recovered method:\n"); //$NON-NLS-1$
	this.methodDeclaration.print(tab + 1, result);
	if (this.annotations != null) {
		for (int i = 0; i < this.annotationCount; i++) {
			result.append("\n"); //$NON-NLS-1$
			result.append(this.annotations[i].toString(tab + 1));
		}
	}
	if (this.localTypes != null) {
		for (int i = 0; i < this.localTypeCount; i++) {
			result.append("\n"); //$NON-NLS-1$
			result.append(this.localTypes[i].toString(tab + 1));
		}
	}
	if (this.methodBody != null) {
		result.append("\n"); //$NON-NLS-1$
		result.append(this.methodBody.toString(tab + 1));
	}
	return result.toString();
}
/*
 * Update the bodyStart of the corresponding parse node
 */
@Override
public void updateBodyStart(int bodyStart){
	this.foundOpeningBrace = true;
	this.methodDeclaration.bodyStart = bodyStart;
}
public AbstractMethodDeclaration updatedMethodDeclaration(int depth, Set<TypeDeclaration> knownTypes){
	/* update annotations */
	if (this.modifiers != 0) {
		this.methodDeclaration.modifiers |= this.modifiers;
		if (this.modifiersStart < this.methodDeclaration.declarationSourceStart) {
			this.methodDeclaration.declarationSourceStart = this.modifiersStart;
		}
	}
	/* update annotations */
	if (this.annotationCount > 0){
		int existingCount = this.methodDeclaration.annotations == null ? 0 : this.methodDeclaration.annotations.length;
		Annotation[] annotationReferences = new Annotation[existingCount + this.annotationCount];
		if (existingCount > 0){
			System.arraycopy(this.methodDeclaration.annotations, 0, annotationReferences, this.annotationCount, existingCount);
		}
		for (int i = 0; i < this.annotationCount; i++){
			annotationReferences[i] = this.annotations[i].updatedAnnotationReference();
		}
		this.methodDeclaration.annotations = annotationReferences;

		int start = this.annotations[0].annotation.sourceStart;
		if (start < this.methodDeclaration.declarationSourceStart) {
			this.methodDeclaration.declarationSourceStart = start;
		}
	}

	if (this.methodBody != null){
		Block block = this.methodBody.updatedBlock(depth, knownTypes);
		if (block != null){
			this.methodDeclaration.statements = block.statements;

			if (this.methodDeclaration.declarationSourceEnd == 0) {
				this.methodDeclaration.declarationSourceEnd = block.sourceEnd;
				this.methodDeclaration.bodyEnd = block.sourceEnd;
			}

			/* first statement might be an explict constructor call destinated to a special slot */
			if (this.methodDeclaration.isConstructor()) {
				ConstructorDeclaration constructor = (ConstructorDeclaration)this.methodDeclaration;
				if (this.methodDeclaration.statements != null
					&& this.methodDeclaration.statements[0] instanceof ExplicitConstructorCall){
					constructor.constructorCall = (ExplicitConstructorCall)this.methodDeclaration.statements[0];
					int length = this.methodDeclaration.statements.length;
					System.arraycopy(
						this.methodDeclaration.statements,
						1,
						(this.methodDeclaration.statements = new Statement[length-1]),
						0,
						length-1);
					}
					if (constructor.constructorCall == null){ // add implicit constructor call
						constructor.constructorCall = SuperReference.implicitSuperConstructorCall();
					}
			}
		}
	} else {
		if (this.methodDeclaration.declarationSourceEnd == 0) {
			if (this.methodDeclaration.sourceEnd + 1 == this.methodDeclaration.bodyStart) {
				// right brace is missing
				this.methodDeclaration.declarationSourceEnd = this.methodDeclaration.sourceEnd;
				this.methodDeclaration.bodyStart = this.methodDeclaration.sourceEnd;
				this.methodDeclaration.bodyEnd = this.methodDeclaration.sourceEnd;
			} else {
				this.methodDeclaration.declarationSourceEnd = this.methodDeclaration.bodyStart;
				this.methodDeclaration.bodyEnd = this.methodDeclaration.bodyStart;
			}
		}
	}
	if (this.localTypeCount > 0) this.methodDeclaration.bits |= ASTNode.HasLocalType;
	return this.methodDeclaration;
}
/*
 * Update the corresponding parse node from parser state which
 * is about to disappear because of restarting recovery
 */
@Override
public void updateFromParserState(){
	// if parent is null then recovery already occured in diet parser.
	if(bodyStartsAtHeaderEnd() && this.parent != null){
		Parser parser = parser();
		/* might want to recover arguments or thrown exceptions */
		if (parser.listLength > 0 && parser.astLengthPtr > 0){ // awaiting interface type references
			/* has consumed the arguments - listed elements must be thrown exceptions */
			if (this.methodDeclaration.sourceEnd == parser.rParenPos) {

				// protection for bugs 15142
				int length = parser.astLengthStack[parser.astLengthPtr];
				int astPtr = parser.astPtr - length;
				boolean canConsume = astPtr >= 0;
				if(canConsume) {
					if((!(parser.astStack[astPtr] instanceof AbstractMethodDeclaration))) {
						canConsume = false;
					}
					for (int i = 1, max = length + 1; i < max; i++) {
						if(!(parser.astStack[astPtr + i ] instanceof TypeReference)) {
							canConsume = false;
							break;
						}
					}
				}
				if (canConsume){
					parser.consumeMethodHeaderThrowsClause();
					// will reset typeListLength to zero
					// thus this check will only be performed on first errorCheck after void foo() throws X, Y,
				} else {
					parser.listLength = 0;
				}
			} else {
				/* has not consumed arguments yet, listed elements must be arguments */
				if (parser.currentToken == TokenNameLPAREN || parser.currentToken == TokenNameSEMICOLON){
					/* if currentToken is parenthesis this last argument is a method/field signature */
					parser.astLengthStack[parser.astLengthPtr] --;
					parser.astPtr --;
					parser.listLength --;
					parser.currentToken = 0;
				}
				int argLength = parser.astLengthStack[parser.astLengthPtr];
				int argStart = parser.astPtr - argLength + 1;
				boolean needUpdateRParenPos = parser.rParenPos < parser.lParenPos; // 12387 : rParenPos will be used

				// remove unfinished annotation nodes
				MemberValuePair[] memberValuePairs = null;
				while (argLength > 0 && parser.astStack[parser.astPtr] instanceof MemberValuePair) {
					System.arraycopy(parser.astStack, argStart, memberValuePairs = new MemberValuePair[argLength], 0, argLength);
					parser.astLengthPtr--;
					parser.astPtr -= argLength;

					argLength = parser.astLengthStack[parser.astLengthPtr];
					argStart = parser.astPtr - argLength + 1;
					needUpdateRParenPos = true;
				}

				// to compute bodyStart, and thus used to set next checkpoint.
				int count;
				for (count = 0; count < argLength; count++){
					ASTNode aNode = parser.astStack[argStart+count];
					if(aNode instanceof Argument) {
						Argument argument = (Argument)aNode;
						/* cannot be an argument if non final */
						char[][] argTypeName = argument.type.getTypeName();
						if ((argument.modifiers & ~ClassFileConstants.AccFinal) != 0
							|| (argTypeName.length == 1
								&& CharOperation.equals(argTypeName[0], TypeBinding.VOID.sourceName()))){
							parser.astLengthStack[parser.astLengthPtr] = count;
							parser.astPtr = argStart+count-1;
							parser.listLength = count;
							parser.currentToken = 0;
							break;
						}
						if (needUpdateRParenPos) parser.rParenPos = argument.sourceEnd + 1;
					} else {
						parser.astLengthStack[parser.astLengthPtr] = count;
						parser.astPtr = argStart+count-1;
						parser.listLength = count;
						parser.currentToken = 0;
						break;
					}
				}
				if (parser.listLength > 0 && parser.astLengthPtr > 0){

					// protection for bugs 15142
					int length = parser.astLengthStack[parser.astLengthPtr];
					int astPtr = parser.astPtr - length;
					boolean canConsume = astPtr >= 0;
					if(canConsume) {
						if((!(parser.astStack[astPtr] instanceof AbstractMethodDeclaration))) {
							canConsume = false;
						}
						for (int i = 1, max = length + 1; i < max; i++) {
							if(!(parser.astStack[astPtr + i ] instanceof Argument)) {
								canConsume = false;
								break;
							}
						}
					}
					if(canConsume) {
						parser.consumeMethodHeaderRightParen();
						/* fix-up positions, given they were updated against rParenPos, which did not get set */
						if (parser.currentElement == this){ // parameter addition might have added an awaiting (no return type) method - see 1FVXQZ4 */
							if (this.methodDeclaration.arguments != null) {
								this.methodDeclaration.sourceEnd = this.methodDeclaration.arguments[this.methodDeclaration.arguments.length-1].sourceEnd;
							} else {
								this.methodDeclaration.sourceEnd = this.methodDeclaration.receiver.sourceEnd;
							}
							this.methodDeclaration.bodyStart = this.methodDeclaration.sourceEnd+1;
							parser.lastCheckPoint = this.methodDeclaration.bodyStart;
						}
					}
				}

				if(memberValuePairs != null) {
					System.arraycopy(memberValuePairs, 0, parser.astStack, parser.astPtr + 1, memberValuePairs.length);
					parser.astPtr += memberValuePairs.length;
					parser.astLengthStack[++parser.astLengthPtr] = memberValuePairs.length;
				}
			}
		}
	}
}
@Override
public RecoveredElement updateOnClosingBrace(int braceStart, int braceEnd){
	if(this.methodDeclaration.isAnnotationMethod()) {
		this.updateSourceEndIfNecessary(braceStart, braceEnd);
		if(!this.foundOpeningBrace && this.parent != null) {
			return this.parent.updateOnClosingBrace(braceStart, braceEnd);
		}
		return this;
	}
	if(this.parent != null && this.parent instanceof RecoveredType) {
		int mods = ((RecoveredType)this.parent).typeDeclaration.modifiers;
		if (TypeDeclaration.kind(mods) == TypeDeclaration.INTERFACE_DECL) {
			if (!this.foundOpeningBrace) {
				this.updateSourceEndIfNecessary(braceStart - 1, braceStart - 1);
				return this.parent.updateOnClosingBrace(braceStart, braceEnd);
			}
		}
	}
	return super.updateOnClosingBrace(braceStart, braceEnd);
}
/*
 * An opening brace got consumed, might be the expected opening one of the current element,
 * in which case the bodyStart is updated.
 */
@Override
public RecoveredElement updateOnOpeningBrace(int braceStart, int braceEnd){

	/* in case the opening brace is close enough to the signature */
	if (this.bracketBalance == 0){
		/*
			if (parser.scanner.searchLineNumber(methodDeclaration.sourceEnd)
				!= parser.scanner.searchLineNumber(braceEnd)){
		 */
		switch(parser().lastIgnoredToken){
			case -1 :
			case TokenNamethrows :
				break;
			default:
				this.foundOpeningBrace = true;
				this.bracketBalance = 1; // pretend the brace was already there
		}
	}
	return super.updateOnOpeningBrace(braceStart, braceEnd);
}
@Override
public void updateParseTree(){
	updatedMethodDeclaration(0, new HashSet<TypeDeclaration>());
}
/*
 * Update the declarationSourceEnd of the corresponding parse node
 */
@Override
public void updateSourceEndIfNecessary(int braceStart, int braceEnd){
	if (this.methodDeclaration.declarationSourceEnd == 0) {
		if(parser().rBraceSuccessorStart >= braceEnd) {
			this.methodDeclaration.declarationSourceEnd = parser().rBraceEnd;
			this.methodDeclaration.bodyEnd = parser().rBraceStart;
		} else {
			this.methodDeclaration.declarationSourceEnd = braceEnd;
			this.methodDeclaration.bodyEnd  = braceStart - 1;
		}
	}
}
@Override
public RecoveredElement addAnnotationName(int identifierPtr, int identifierLengthPtr, int annotationStart, int bracketBalanceValue) {
	if (this.pendingAnnotations == null) {
		this.pendingAnnotations = new RecoveredAnnotation[5];
		this.pendingAnnotationCount = 0;
	} else {
		if (this.pendingAnnotationCount == this.pendingAnnotations.length) {
			System.arraycopy(
				this.pendingAnnotations,
				0,
				(this.pendingAnnotations = new RecoveredAnnotation[2 * this.pendingAnnotationCount]),
				0,
				this.pendingAnnotationCount);
		}
	}

	RecoveredAnnotation element = new RecoveredAnnotation(identifierPtr, identifierLengthPtr, annotationStart, this, bracketBalanceValue);

	this.pendingAnnotations[this.pendingAnnotationCount++] = element;

	return element;
}
@Override
public void addModifier(int flag, int modifiersSourceStart) {
	this.pendingModifiers |= flag;

	if (this.pendingModifersSourceStart < 0) {
		this.pendingModifersSourceStart = modifiersSourceStart;
	}
}
void attach(TypeParameter[] parameters, int startPos) {
	if(this.methodDeclaration.modifiers != ClassFileConstants.AccDefault) return;

	int lastParameterEnd = parameters[parameters.length - 1].sourceEnd;

	Parser parser = parser();
	Scanner scanner = parser.scanner;
	if(Util.getLineNumber(this.methodDeclaration.declarationSourceStart, scanner.lineEnds, 0, scanner.linePtr)
			!= Util.getLineNumber(lastParameterEnd, scanner.lineEnds, 0, scanner.linePtr)) return;

	if(parser.modifiersSourceStart > lastParameterEnd
			&& parser.modifiersSourceStart < this.methodDeclaration.declarationSourceStart) return;

	if (this.methodDeclaration instanceof MethodDeclaration) {
		((MethodDeclaration)this.methodDeclaration).typeParameters = parameters;
		this.methodDeclaration.declarationSourceStart = startPos;
	} else if (this.methodDeclaration instanceof ConstructorDeclaration){
		((ConstructorDeclaration)this.methodDeclaration).typeParameters = parameters;
		this.methodDeclaration.declarationSourceStart = startPos;
	}
}
public void attach(RecoveredAnnotation[] annots, int annotCount, int mods, int modsSourceStart) {
	if (annotCount > 0) {
		Annotation[] existingAnnotations = this.methodDeclaration.annotations;
		if (existingAnnotations != null) {
			this.annotations = new RecoveredAnnotation[annotCount];
			this.annotationCount = 0;
			next : for (int i = 0; i < annotCount; i++) {
				for (int j = 0; j < existingAnnotations.length; j++) {
					if (annots[i].annotation == existingAnnotations[j]) continue next;
				}
				this.annotations[this.annotationCount++] = annots[i];
			}
		} else {
			this.annotations = annots;
			this.annotationCount = annotCount;
		}
	}

	if (mods != 0) {
		this.modifiers = mods;
		this.modifiersStart = modsSourceStart;
	}
}
}
