gt: 		long argNamePos = -1;
context: /*******************************************************************************
   * Copyright (c) 2000, 2018 IBM Corporation and others.
   *
   * This program and the accompanying materials
   * are made available under the terms of the Eclipse Public License 2.0
   * which accompanies this distribution, and is available at
   * https://www.eclipse.org/legal/epl-2.0/
   *
   * SPDX-License-Identifier: EPL-2.0
   *
   * Contributors:
   *     IBM Corporation - initial API and implementation
   *******************************************************************************/
  package org.eclipse.jdt.internal.codeassist.complete;
  
  import java.util.ArrayList;
  import java.util.List;
  
  import org.eclipse.jdt.core.compiler.CharOperation;
  import org.eclipse.jdt.core.compiler.InvalidInputException;
  import org.eclipse.jdt.internal.codeassist.CompletionEngine;
  import org.eclipse.jdt.internal.compiler.ast.*;
  import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
  import org.eclipse.jdt.internal.compiler.parser.JavadocParser;
  import org.eclipse.jdt.internal.compiler.parser.ScannerHelper;
  import org.eclipse.jdt.internal.compiler.parser.TerminalTokens;
  
  /**
   * Parser specialized for decoding javadoc comments which includes cursor location for code completion.
   */
  @SuppressWarnings({"rawtypes", "unchecked"})
  public class CompletionJavadocParser extends JavadocParser {
  
  	// Initialize lengthes for block and inline tags tables
  	public final static int INLINE_ALL_TAGS_LENGTH;
  	public final static int BLOCK_ALL_TAGS_LENGTH;
  	static {
  		int length = 0;
  		for (int i=0; i<INLINE_TAGS_LENGTH; i++) {
  			length += INLINE_TAGS[i].length;
  		}
  		INLINE_ALL_TAGS_LENGTH  = length;
  		length = 0;
  		for (int i=0; i<BLOCK_TAGS_LENGTH; i++) {
  			length += BLOCK_TAGS[i].length;
  		}
  		BLOCK_ALL_TAGS_LENGTH = length;
  	}
  
  	// Level tags are array of inline/block tags depending on compilation source level
  	char[][][] levelTags = new char[2][][];
  	int[] levelTagsLength = new int[2];
  
  	// Completion specific info
  	int cursorLocation;
  	CompletionOnJavadoc completionNode = null;
  	boolean pushText = false;
  	boolean allPossibleTags = false;
  
  	public CompletionJavadocParser(CompletionParser sourceParser) {
  		super(sourceParser);
  		this.scanner = new CompletionScanner(ClassFileConstants.JDK1_3);
  		this.kind = COMPLETION_PARSER | TEXT_PARSE;
  		initLevelTags();
  	}
  
  	/*
  	 * Do not parse comment if completion location is not included.
  	 */
  	@Override
  	public boolean checkDeprecation(int commentPtr) {
  		boolean isDeprecated = false;
  
  		this.cursorLocation = ((CompletionParser)this.sourceParser).cursorLocation;
  		CompletionScanner completionScanner = (CompletionScanner)this.scanner;
  		completionScanner.cursorLocation = this.cursorLocation;
  		this.javadocStart = this.sourceParser.scanner.commentStarts[commentPtr];
  		this.javadocEnd = this.sourceParser.scanner.commentStops[commentPtr];
  		if (this.javadocStart <= this.cursorLocation && this.cursorLocation <= this.javadocEnd) {
  			if (CompletionEngine.DEBUG) {
  				System.out.println("COMPLETION in Javadoc:"); //$NON-NLS-1$
  			}
  			completionScanner.completionIdentifier = null;
  			super.checkDeprecation(commentPtr);
  		} else {
  			if (this.sourceParser.scanner.commentTagStarts[commentPtr] != 0) {
  				boolean previousValue = this.checkDocComment;
  				this.checkDocComment = false;
  				isDeprecated = super.checkDeprecation(commentPtr);
  				this.checkDocComment = previousValue;
  			}
  			this.docComment = null;
  		}
  		return isDeprecated;
  	}
  
  	/*
  	 * Replace stored Javadoc node with specific completion one.
  	 */
  	@Override
  	protected boolean commentParse() {
  		this.docComment = new CompletionJavadoc(this.javadocStart, this.javadocEnd);
  		this.firstTagPosition = 1; // bug 429340: completion parser needs to parse the whole comment
  		return super.commentParse();
  	}
  
  	/*
  	 * Create argument expression. If it includes completion location, create and store completion node.
  	 */
  	@Override
  	protected Object createArgumentReference(char[] name, int dim, boolean isVarargs, Object typeRef, long[] dimPositions, long argNamePos) throws InvalidInputException {
  		// Create argument as we may need it after
  		char[] argName = name==null ? CharOperation.NO_CHAR : name;
  		Expression expression = (Expression) super.createArgumentReference(argName, dim, isVarargs, typeRef, dimPositions, argNamePos);
  		// See if completion location is in argument
  		int refStart = ((TypeReference)typeRef).sourceStart;
  		int refEnd = ((TypeReference)typeRef).sourceEnd;
  		boolean inCompletion = (refStart <= this.cursorLocation && this.cursorLocation <= refEnd) // completion cursor is between first and last stacked identifiers
  			|| ((refStart == (refEnd+1) && refEnd == this.cursorLocation)); // or it's a completion on empty token
  		if (this.completionNode == null && inCompletion) {
  			JavadocArgumentExpression javadocArgument = (JavadocArgumentExpression) expression;
  			TypeReference expressionType = javadocArgument.argument.type;
  			if (expressionType instanceof JavadocSingleTypeReference) {
  				this.completionNode = new CompletionOnJavadocSingleTypeReference((JavadocSingleTypeReference) expressionType);
  			} else if (expressionType instanceof JavadocQualifiedTypeReference) {
  				this.completionNode = new CompletionOnJavadocQualifiedTypeReference((JavadocQualifiedTypeReference) expressionType);
  			}
  			if (CompletionEngine.DEBUG) {
  				System.out.println("	completion argument="+this.completionNode); //$NON-NLS-1$
  			}
  			return this.completionNode;
  		}
  		return expression;
  	}
  
  	/*
  	 * Create field reference. If it includes completion location, create and store completion node.
  	 */
  	@Override
  	protected Object createFieldReference(Object receiver) throws InvalidInputException {
  		int refStart = (int) (this.identifierPositionStack[0] >>> 32);
  		int refEnd = (int) this.identifierPositionStack[0];
  		boolean inCompletion = (refStart <= (this.cursorLocation+1) && this.cursorLocation <= refEnd) // completion cursor is between first and last stacked identifiers
  			|| ((refStart == (refEnd+1) && refEnd == this.cursorLocation)) // or it's a completion on empty token
  			|| (this.memberStart == this.cursorLocation); // or it's a completion just after the member separator with an identifier after the cursor
  		if (inCompletion) {
  			JavadocFieldReference fieldRef = (JavadocFieldReference) super.createFieldReference(receiver);
  			char[] name = this.sourceParser.compilationUnit.getMainTypeName();
  			TypeDeclaration typeDecl = getParsedTypeDeclaration();
  			if (typeDecl != null) {
  				name = typeDecl.name;
  			}
  			this.completionNode = new CompletionOnJavadocFieldReference(fieldRef, this.memberStart, name);
  			if (CompletionEngine.DEBUG) {
  				System.out.println("	completion field="+this.completionNode); //$NON-NLS-1$
  			}
  			return this.completionNode;
  		}
  		return super.createFieldReference(receiver);
  	}
  
  	/*
  	 * Verify if method identifier positions include completion location.
  	 * If so, create method reference and store it.
  	 * Otherwise return null as we do not need this reference.
  	 */
  	@Override
  	protected Object createMethodReference(Object receiver, List arguments) throws InvalidInputException {
  		int memberPtr = this.identifierLengthStack[0] - 1; // may be > 0 for inner class constructor reference
  		int refStart = (int) (this.identifierPositionStack[memberPtr] >>> 32);
  		int refEnd = (int) this.identifierPositionStack[memberPtr];
  		boolean inCompletion = (refStart <= (this.cursorLocation+1) && this.cursorLocation <= refEnd) // completion cursor is between first and last stacked identifiers
  			|| ((refStart == (refEnd+1) && refEnd == this.cursorLocation)) // or it's a completion on empty token
  			|| (this.memberStart == this.cursorLocation); // or it's a completion just after the member separator with an identifier after the cursor
  		if (inCompletion) {
  			ASTNode node = (ASTNode) super.createMethodReference(receiver, arguments);
  			if (node instanceof JavadocMessageSend) {
  				JavadocMessageSend messageSend = (JavadocMessageSend) node;
  				int nameStart = (int) (messageSend.nameSourcePosition >>> 32);
  				int nameEnd = (int) messageSend.nameSourcePosition;
  				if ((nameStart <= (this.cursorLocation+1) && this.cursorLocation <= nameEnd)) {
  					this.completionNode = new CompletionOnJavadocFieldReference(messageSend, this.memberStart);
  				} else {
  					this.completionNode = new CompletionOnJavadocMessageSend(messageSend, this.memberStart);
  				}
  			} else if (node instanceof JavadocAllocationExpression) {
  				this.completionNode = new CompletionOnJavadocAllocationExpression((JavadocAllocationExpression)node, this.memberStart);
  			}
  			if (CompletionEngine.DEBUG) {
  				System.out.println("	completion method="+this.completionNode); //$NON-NLS-1$
  			}
  			return this.completionNode;
  		}
  		return super.createMethodReference(receiver, arguments);
  	}
  
  	/*
  	 * Create type reference. If it includes completion location, create and store completion node.
  	 */
  	@Override
  	protected Object createTypeReference(int primitiveToken) {
  		// Need to create type ref in case it was needed by members
  		int nbIdentifiers = this.identifierLengthStack[this.identifierLengthPtr];
  		int startPtr = this.identifierPtr - (nbIdentifiers-1);
  		int refStart = (int) (this.identifierPositionStack[startPtr] >>> 32);
  		int refEnd = (int) this.identifierPositionStack[this.identifierPtr];
  		boolean inCompletion = (refStart <= (this.cursorLocation+1) && this.cursorLocation <= refEnd) // completion cursor is between first and last stacked identifiers
  			|| ((refStart == (refEnd+1) && refEnd == this.cursorLocation)); // or it's a completion on empty token
  		if (!inCompletion) {
  			return super.createTypeReference(primitiveToken);
  		}
  		this.identifierLengthPtr--;
  		if (nbIdentifiers == 1) { // Single Type ref
  			this.completionNode = new CompletionOnJavadocSingleTypeReference(
  						this.identifierStack[this.identifierPtr],
  						this.identifierPositionStack[this.identifierPtr],
  						this.tagSourceStart,
  						this.tagSourceEnd);
  		} else if (nbIdentifiers > 1) { // Qualified Type ref
  			for (int i=startPtr; i<this.identifierPtr; i++) {
  				int start = (int) (this.identifierPositionStack[i] >>> 32);
  				int end = (int) this.identifierPositionStack[i];
  				if (start <= this.cursorLocation && this.cursorLocation <= end) {
  					if (i == startPtr) {
  						this.completionNode = new CompletionOnJavadocSingleTypeReference(
  									this.identifierStack[startPtr],
  									this.identifierPositionStack[startPtr],
  									this.tagSourceStart,
  									this.tagSourceEnd);
  					} else {
  						char[][] tokens = new char[i][];
  						System.arraycopy(this.identifierStack, startPtr, tokens, 0, i);
  						long[] positions = new long[i+1];
  						System.arraycopy(this.identifierPositionStack, startPtr, positions, 0, i+1);
  						this.completionNode = new CompletionOnJavadocQualifiedTypeReference(tokens, this.identifierStack[i], positions, this.tagSourceStart, this.tagSourceEnd);
  					}
  					break;
  				}
  			}
  			if (this.completionNode == null) {
  				char[][] tokens = new char[nbIdentifiers-1][];
  				System.arraycopy(this.identifierStack, startPtr, tokens, 0, nbIdentifiers-1);
  				long[] positions = new long[nbIdentifiers];
  				System.arraycopy(this.identifierPositionStack, startPtr, positions, 0, nbIdentifiers);
  				this.completionNode = new CompletionOnJavadocQualifiedTypeReference(tokens, this.identifierStack[this.identifierPtr], positions, this.tagSourceStart, this.tagSourceEnd);
  			}
  		}
  
  		if (CompletionEngine.DEBUG) {
  			System.out.println("	completion partial qualified type="+this.completionNode); //$NON-NLS-1$
  		}
  		return this.completionNode;
  	}
  
  	/*
  	 * Get possible tags for a given prefix.
  	 */
  	private char[][][] possibleTags(char[] prefix, boolean newLine) {
  		char[][][] possibleTags = new char[2][][];
  		if (newLine) {
  			System.arraycopy(this.levelTags[BLOCK_IDX], 0, possibleTags[BLOCK_IDX] = new char[this.levelTagsLength[BLOCK_IDX]][], 0, this.levelTagsLength[BLOCK_IDX]);
  		} else {
  			possibleTags[BLOCK_IDX] = CharOperation.NO_CHAR_CHAR;
  		}
  		System.arraycopy(this.levelTags[INLINE_IDX], 0, possibleTags[INLINE_IDX] = new char[this.levelTagsLength[INLINE_IDX]][], 0, this.levelTagsLength[INLINE_IDX]);
  		if (prefix == null || prefix.length == 0) return possibleTags;
  		int kinds = this.levelTags.length;
  		for (int k=0; k<kinds; k++) {
  			int length = possibleTags[k].length, size = 0;
  			int indexes[] = new int[length];
  			for (int i=0; i<length; i++) {
  				if (CharOperation.prefixEquals(prefix, possibleTags[k][i], false)) {
  					indexes[size++] = i;
  				}
  			}
  			char[][] tags = new char[size][];
  			for (int i=0; i<size; i++) {
  				tags[i] = possibleTags[k][indexes[i]];
  			}
  			possibleTags[k] = tags;
  		}
  		return possibleTags;
  	}
  
  	private CompletionJavadoc getCompletionJavadoc() {
  		return (CompletionJavadoc)this.docComment;
  	}
  
  	private CompletionParser getCompletionParser() {
  		return (CompletionParser)this.sourceParser;
  	}
  
  	/*
  	 * Init tags arrays for current source level.
  	 */
  	private void initLevelTags() {
  		int level = ((int)(this.complianceLevel >>> 16)) - ClassFileConstants.MAJOR_VERSION_1_1 + 1;
  		if ( level >= BLOCK_TAGS_LENGTH)
  			return; // To support future JDKs
  		// Init block tags
  		this.levelTags[BLOCK_IDX] = new char[BLOCK_ALL_TAGS_LENGTH][];
  		this.levelTagsLength[BLOCK_IDX] = 0;
  		for (int i=0; i<=level; i++) {
  			int length = BLOCK_TAGS[i].length;
  			System.arraycopy(BLOCK_TAGS[i], 0, this.levelTags[BLOCK_IDX], this.levelTagsLength[BLOCK_IDX], length);
  			this.levelTagsLength[BLOCK_IDX] += length;
  		}
  		if (this.levelTagsLength[BLOCK_IDX] < BLOCK_ALL_TAGS_LENGTH) {
  			System.arraycopy(this.levelTags[BLOCK_IDX], 0, this.levelTags[BLOCK_IDX] = new char[this.levelTagsLength[BLOCK_IDX]][], 0, this.levelTagsLength[BLOCK_IDX]);
  		}
  		// Init inline tags
  		this.levelTags[INLINE_IDX] = new char[INLINE_ALL_TAGS_LENGTH][];
  		this.levelTagsLength[INLINE_IDX]= 0;
  		for (int i=0; i<=level; i++) {
  			int length = INLINE_TAGS[i].length;
  			System.arraycopy(INLINE_TAGS[i], 0, this.levelTags[INLINE_IDX], this.levelTagsLength[INLINE_IDX], length);
  			this.levelTagsLength[INLINE_IDX] += length;
  		}
  		if (this.levelTagsLength[INLINE_IDX] < INLINE_ALL_TAGS_LENGTH) {
  			System.arraycopy(this.levelTags[INLINE_IDX], 0, this.levelTags[INLINE_IDX] = new char[this.levelTagsLength[INLINE_IDX]][], 0, this.levelTagsLength[INLINE_IDX]);
  		}
  	}
  	/*
  	 * Parse argument in @see tag method reference
  	 */
  	@Override
  	protected Object parseArguments(Object receiver) throws InvalidInputException {
  
  		if (this.tagSourceStart>this.cursorLocation) {
  			return super.parseArguments(receiver);
  		}
  
  		// Init
  		int modulo = 0; // should be 2 for (Type,Type,...) or 3 for (Type arg,Type arg,...)
  		int iToken = 0;
  		char[] argName = null;
  		List arguments = new ArrayList(10);
  		Object typeRef = null;
  		int dim = 0;
  		boolean isVarargs = false;
  		long[] dimPositions = new long[20]; // assume that there won't be more than 20 dimensions...
  		char[] name = null;
  