gt: 				if (!this.inlineTagStarted) {
context: /*******************************************************************************
   * Copyright (c) 2000, 2011 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.parser;
  
  import java.util.List;
  
  import org.eclipse.jdt.core.compiler.CharOperation;
  import org.eclipse.jdt.core.compiler.InvalidInputException;
  import org.eclipse.jdt.internal.compiler.ast.*;
  import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
  import org.eclipse.jdt.internal.compiler.util.Util;
  
  /**
   * Parser specialized for decoding javadoc comments
   */
  public class JavadocParser extends AbstractCommentParser {
  
  	// Public fields
  	public Javadoc docComment;
  
  	// bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=51600
  	// Store param references for tag with invalid syntax
  	private int invalidParamReferencesPtr = -1;
  	private ASTNode[] invalidParamReferencesStack;
  
  	// bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=153399
  	// Store value tag positions
  	private long validValuePositions, invalidValuePositions;
  
  	// returns whether this JavadocParser should report errors or not (overrides reportProblems)
  	// see "https://bugs.eclipse.org/bugs/show_bug.cgi?id=192449"
  	public boolean shouldReportProblems = true;
  	
  	// flag to let the parser know that the current tag is waiting for a description
  	// see "https://bugs.eclipse.org/bugs/show_bug.cgi?id=222900"
  	private int tagWaitingForDescription;
  
  	public JavadocParser(Parser sourceParser) {
  		super(sourceParser);
  		this.kind = COMPIL_PARSER | TEXT_VERIF;
  		if (sourceParser != null && sourceParser.options != null) {
  			this.setJavadocPositions = sourceParser.options.processAnnotations;
  		}
  	}
  
  	/* (non-Javadoc)
  	 * Returns true if tag @deprecated is present in javadoc comment.
  	 *
  	 * If javadoc checking is enabled, will also construct an Javadoc node, which will be stored into Parser.javadoc
  	 * slot for being consumed later on.
  	 */
  	public boolean checkDeprecation(int commentPtr) {
  
  		// Store javadoc positions
  		this.javadocStart = this.sourceParser.scanner.commentStarts[commentPtr];
  		this.javadocEnd = this.sourceParser.scanner.commentStops[commentPtr]-1;
  		this.firstTagPosition = this.sourceParser.scanner.commentTagStarts[commentPtr];
  		this.validValuePositions = -1;
  		this.invalidValuePositions = -1;
  		this.tagWaitingForDescription = NO_TAG_VALUE;
  
  		// Init javadoc if necessary
  		if (this.checkDocComment) {
  			this.docComment = new Javadoc(this.javadocStart, this.javadocEnd);
  		} else if (this.setJavadocPositions) {
  			// https://bugs.eclipse.org/bugs/show_bug.cgi?id=189459
  			// if annotation processors are there, javadoc object is required but
  			// they need not be resolved
  			this.docComment = new Javadoc(this.javadocStart, this.javadocEnd);
  			this.docComment.bits &= ~ASTNode.ResolveJavadoc;
  		} else {
  			this.docComment = null;
  		}
  
  		// If there's no tag in javadoc, return without parsing it
  		if (this.firstTagPosition == 0) {
  			switch (this.kind & PARSER_KIND) {
  				case COMPIL_PARSER:
  				case SOURCE_PARSER:
  					return false;
  			}
  		}
  
  		// Parse
  		try {
  			this.source = this.sourceParser.scanner.source;
  			if (this.checkDocComment) {
  				// Initialization
  				this.scanner.lineEnds = this.sourceParser.scanner.lineEnds;
  				this.scanner.linePtr = this.sourceParser.scanner.linePtr;
  				this.lineEnds = this.scanner.lineEnds;
  				commentParse();
  			} else {
  
  				// Parse comment
  				Scanner sourceScanner = this.sourceParser.scanner;
  				int firstLineNumber = Util.getLineNumber(this.javadocStart, sourceScanner.lineEnds, 0, sourceScanner.linePtr);
  				int lastLineNumber = Util.getLineNumber(this.javadocEnd, sourceScanner.lineEnds, 0, sourceScanner.linePtr);
  				this.index = this.javadocStart +3;
  
  				// scan line per line, since tags must be at beginning of lines only
  				this.deprecated = false;
  				nextLine : for (int line = firstLineNumber; line <= lastLineNumber; line++) {
  					int lineStart = line == firstLineNumber
  							? this.javadocStart + 3 // skip leading /**
  							: this.sourceParser.scanner.getLineStart(line);
  					this.index = lineStart;
  					this.lineEnd = line == lastLineNumber
  							? this.javadocEnd - 2 // remove trailing * /
  							: this.sourceParser.scanner.getLineEnd(line);
  					nextCharacter : while (this.index < this.lineEnd) {
  						char c = readChar(); // consider unicodes
  						switch (c) {
  							case '*' :
  							case '\u000c' :	/* FORM FEED               */
  							case ' ' :			/* SPACE                   */
  							case '\t' :			/* HORIZONTAL TABULATION   */
  							case '\n' :			/* LINE FEED   */
  							case '\r' :			/* CR */
  								// do nothing for space or '*' characters
  						        continue nextCharacter;
  						    case '@' :
  						    	parseSimpleTag();
  						    	if (this.tagValue == TAG_DEPRECATED_VALUE) {
  						    		if (this.abort) break nextCharacter;
  						    	}
  						}
  			        	continue nextLine;
  					}
  				}
  				return this.deprecated;
  			}
  		} finally {
  			this.source = null; // release source as soon as finished
  		}
  		return this.deprecated;
  	}
  
  	/* (non-Javadoc)
  	 * @see org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser#createArgumentReference(char[], java.lang.Object, int)
  	 */
  	protected Object createArgumentReference(char[] name, int dim, boolean isVarargs, Object typeRef, long[] dimPositions, long argNamePos) throws InvalidInputException {
  		try {
  			TypeReference argTypeRef = (TypeReference) typeRef;
  			if (dim > 0) {
  				long pos = (((long) argTypeRef.sourceStart) << 32) + argTypeRef.sourceEnd;
  				if (typeRef instanceof JavadocSingleTypeReference) {
  					JavadocSingleTypeReference singleRef = (JavadocSingleTypeReference) typeRef;
  					argTypeRef = new JavadocArraySingleTypeReference(singleRef.token, dim, pos);
  				} else {
  					JavadocQualifiedTypeReference qualifRef = (JavadocQualifiedTypeReference) typeRef;
  					argTypeRef = new JavadocArrayQualifiedTypeReference(qualifRef, dim);
  				}
  			}
  			int argEnd = argTypeRef.sourceEnd;
  			if (dim > 0) {
  				argEnd = (int) dimPositions[dim-1];
  				if (isVarargs) {
  					argTypeRef.bits |= ASTNode.IsVarArgs; // set isVarArgs
  				}
  			}
  			if (argNamePos >= 0) argEnd = (int) argNamePos;
  			return new JavadocArgumentExpression(name, argTypeRef.sourceStart, argEnd, argTypeRef);
  		}
  		catch (ClassCastException ex) {
  			throw new InvalidInputException();
  		}
  	}
  	/* (non-Javadoc)
  	 * @see org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser#createFieldReference()
  	 */
  	protected Object createFieldReference(Object receiver) throws InvalidInputException {
  		try {
  			// Get receiver type
  			TypeReference typeRef = (TypeReference) receiver;
  			if (typeRef == null) {
  				char[] name = this.sourceParser.compilationUnit.getMainTypeName();
  				typeRef = new JavadocImplicitTypeReference(name, this.memberStart);
  			}
  			// Create field
  			JavadocFieldReference field = new JavadocFieldReference(this.identifierStack[0], this.identifierPositionStack[0]);
  			field.receiver = typeRef;
  			field.tagSourceStart = this.tagSourceStart;
  			field.tagSourceEnd = this.tagSourceEnd;
  			field.tagValue = this.tagValue;
  			return field;
  		}
  		catch (ClassCastException ex) {
  			throw new InvalidInputException();
  		}
  	}
  	/* (non-Javadoc)
  	 * @see org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser#createMethodReference(java.lang.Object[])
  	 */
  	protected Object createMethodReference(Object receiver, List arguments) throws InvalidInputException {
  		try {
  			// Get receiver type
  			TypeReference typeRef = (TypeReference) receiver;
  			// Decide whether we have a constructor or not
  			boolean isConstructor = false;
  			int length = this.identifierLengthStack[0];	// may be > 1 for member class constructor reference
  			if (typeRef == null) {
  				char[] name = this.sourceParser.compilationUnit.getMainTypeName();
  				TypeDeclaration typeDecl = getParsedTypeDeclaration();
  				if (typeDecl != null) {
  					name = typeDecl.name;
  				}
  				isConstructor = CharOperation.equals(this.identifierStack[length-1], name);
  				typeRef = new JavadocImplicitTypeReference(name, this.memberStart);
  			} else {
  				if (typeRef instanceof JavadocSingleTypeReference) {
  					char[] name = ((JavadocSingleTypeReference)typeRef).token;
  					isConstructor = CharOperation.equals(this.identifierStack[length-1], name);
  				} else if (typeRef instanceof JavadocQualifiedTypeReference) {
  					char[][] tokens = ((JavadocQualifiedTypeReference)typeRef).tokens;
  					int last = tokens.length-1;
  					isConstructor = CharOperation.equals(this.identifierStack[length-1], tokens[last]);
  					if (isConstructor) {
  						boolean valid = true;
  						if (valid) {
  							for (int i=0; i<length-1 && valid; i++) {
  								valid = CharOperation.equals(this.identifierStack[i], tokens[i]);
  							}
  						}
  						if (!valid) {
  							if (this.reportProblems) {
  								this.sourceParser.problemReporter().javadocInvalidMemberTypeQualification((int)(this.identifierPositionStack[0]>>>32), (int)this.identifierPositionStack[length-1], -1);
  							}
  							return null;
  						}
  					}
  				} else {
  					throw new InvalidInputException();
  				}
  			}
  			// Create node
  			if (arguments == null) {
  				if (isConstructor) {
  					JavadocAllocationExpression allocation = new JavadocAllocationExpression(this.identifierPositionStack[length-1]);
  					allocation.type = typeRef;
  					allocation.tagValue = this.tagValue;
  					allocation.sourceEnd = this.scanner.getCurrentTokenEndPosition();
  					if (length == 1) {
  						allocation.qualification = new char[][] { this.identifierStack[0] };
  					} else {
  						System.arraycopy(this.identifierStack, 0, allocation.qualification = new char[length][], 0, length);
  						allocation.sourceStart = (int) (this.identifierPositionStack[0] >>> 32);
  					}
  					allocation.memberStart = this.memberStart;
  					return allocation;
  				} else {
  					JavadocMessageSend msg = new JavadocMessageSend(this.identifierStack[length-1], this.identifierPositionStack[length-1]);
  					msg.receiver = typeRef;
  					msg.tagValue = this.tagValue;
  					msg.sourceEnd = this.scanner.getCurrentTokenEndPosition();
  					return msg;
  				}
  			} else {
  				JavadocArgumentExpression[] expressions = new JavadocArgumentExpression[arguments.size()];
  				arguments.toArray(expressions);
  				if (isConstructor) {
  					JavadocAllocationExpression allocation = new JavadocAllocationExpression(this.identifierPositionStack[length-1]);
  					allocation.arguments = expressions;
  					allocation.type = typeRef;
  					allocation.tagValue = this.tagValue;
  					allocation.sourceEnd = this.scanner.getCurrentTokenEndPosition();
  					if (length == 1) {
  						allocation.qualification = new char[][] { this.identifierStack[0] };
  					} else {
  						System.arraycopy(this.identifierStack, 0, allocation.qualification = new char[length][], 0, length);
  						allocation.sourceStart = (int) (this.identifierPositionStack[0] >>> 32);
  					}
  					allocation.memberStart = this.memberStart;
  					return allocation;
  				} else {
  					JavadocMessageSend msg = new JavadocMessageSend(this.identifierStack[length-1], this.identifierPositionStack[length-1], expressions);
  					msg.receiver = typeRef;
  					msg.tagValue = this.tagValue;
  					msg.sourceEnd = this.scanner.getCurrentTokenEndPosition();
  					return msg;
  				}
  			}
  		}
  		catch (ClassCastException ex) {
  			throw new InvalidInputException();
  		}
  	}
  	/* (non-Javadoc)
  	 * @see org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser#createReturnStatement()
  	 */
  	protected Object createReturnStatement() {
  		return new JavadocReturnStatement(this.scanner.getCurrentTokenStartPosition(),
  					this.scanner.getCurrentTokenEndPosition());
  	}
  
  	/* (non-Javadoc)
  	 * @see org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser#parseTagName()
  	 */
  	protected void createTag() {
  		this.tagValue = TAG_OTHERS_VALUE;
  	}
  
  	/* (non-Javadoc)
  	 * @see org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser#createTypeReference()
  	 */
  	protected Object createTypeReference(int primitiveToken) {
  		TypeReference typeRef = null;
  		int size = this.identifierLengthStack[this.identifierLengthPtr];
  		if (size == 1) { // Single Type ref
  			typeRef = new JavadocSingleTypeReference(
  						this.identifierStack[this.identifierPtr],
  						this.identifierPositionStack[this.identifierPtr],
  						this.tagSourceStart,
  						this.tagSourceEnd);
  		} else if (size > 1) { // Qualified Type ref
  			char[][] tokens = new char[size][];
  			System.arraycopy(this.identifierStack, this.identifierPtr - size + 1, tokens, 0, size);
  			long[] positions = new long[size];
  			System.arraycopy(this.identifierPositionStack, this.identifierPtr - size + 1, positions, 0, size);
  			typeRef = new JavadocQualifiedTypeReference(tokens, positions, this.tagSourceStart, this.tagSourceEnd);
  		}
  		return typeRef;
  	}
  
  	/*
  	 * Get current parsed type declaration.
  	 */
  	protected TypeDeclaration getParsedTypeDeclaration() {
  		int ptr = this.sourceParser.astPtr;
  		while (ptr >= 0) {
  			Object node = this.sourceParser.astStack[ptr];
  			if (node instanceof TypeDeclaration) {
  				TypeDeclaration typeDecl = (TypeDeclaration) node;
  				if (typeDecl.bodyEnd == 0) { // type declaration currenly parsed
  					return typeDecl;
  				}
  			}
  			ptr--;
  		}
  		return null;
  	}
  
  	/*
  	 * Parse @throws tag declaration and flag missing description if corresponding option is enabled
  	 */
  	protected boolean parseThrows() {
  		boolean valid = super.parseThrows();
  		this.tagWaitingForDescription = valid && this.reportProblems ? TAG_THROWS_VALUE : NO_TAG_VALUE;
  		return valid;
  	}
  
  	/*
  	 * Parse @return tag declaration
  	 */
  	protected boolean parseReturn() {
  		if (this.returnStatement == null) {
  			this.returnStatement = createReturnStatement();
  			return true;
  		}
  		if (this.reportProblems) {
  			this.sourceParser.problemReporter().javadocDuplicatedReturnTag(
  				this.scanner.getCurrentTokenStartPosition(),
  				this.scanner.getCurrentTokenEndPosition());
  		}
  		return false;
  	}
  
  
  	protected void parseSimpleTag() {
  
  		// Read first char
  		// readChar() code is inlined to balance additional method call in checkDeprectation(int)
  		char first = this.source[this.index++];
  		if (first == '\\' && this.source[this.index] == 'u') {
  			int c1, c2, c3, c4;
  			int pos = this.index;
  			this.index++;
  			while (this.source[this.index] == 'u')
  				this.index++;
  			if (!(((c1 = ScannerHelper.getHexadecimalValue(this.source[this.index++])) > 15 || c1 < 0)
  					|| ((c2 = ScannerHelper.getHexadecimalValue(this.source[this.index++])) > 15 || c2 < 0)
  					|| ((c3 = ScannerHelper.getHexadecimalValue(this.source[this.index++])) > 15 || c3 < 0)
  					|| ((c4 = ScannerHelper.getHexadecimalValue(this.source[this.index++])) > 15 || c4 < 0))) {
  				first = (char) (((c1 * 16 + c2) * 16 + c3) * 16 + c4);
  			} else {
  				this.index = pos;
  			}
  		}
  
  		// switch on first tag char
  		switch (first) {
  			case 'd':
  		        if ((readChar() == 'e') &&
  						(readChar() == 'p') && (readChar() == 'r') &&
  						(readChar() == 'e') && (readChar() == 'c') &&
  						(readChar() == 'a') && (readChar() == 't') &&
  						(readChar() == 'e') && (readChar() == 'd')) {
  					// ensure the tag is properly ended: either followed by a space, a tab, line end or asterisk.
  					char c = readChar();
  					if (ScannerHelper.isWhitespace(c) || c == '*') {
  						this.abort = true;
  			    		this.deprecated = true;
  						this.tagValue = TAG_DEPRECATED_VALUE;
  					}
  		        }
  				break;
  		}
  	}
  
  	protected boolean parseTag(int previousPosition) throws InvalidInputException {
  
  		// Complain when tag is missing a description
  		// Note that if the parse of an inline tag has already started, consider it
  		// as the expected description, hence do not report any warning
  		switch (this.tagWaitingForDescription) {
  			case TAG_PARAM_VALUE:
  			case TAG_THROWS_VALUE:
  