blob: 2ffd7355a7316ac9bd6601549835963462970edf [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2016 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.aspectj.org.eclipse.jdt.core.dom;
import java.util.Iterator;
import java.util.List;
import org.aspectj.org.eclipse.jdt.core.compiler.CharOperation;
import org.aspectj.org.eclipse.jdt.core.compiler.InvalidInputException;
import org.aspectj.org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.aspectj.org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser;
import org.aspectj.org.eclipse.jdt.internal.compiler.parser.Scanner;
import org.aspectj.org.eclipse.jdt.internal.compiler.parser.ScannerHelper;
import org.aspectj.org.eclipse.jdt.internal.compiler.parser.TerminalTokens;
/**
* Internal parser used for decoding doc comments.
*
* @since 3.0
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
class DocCommentParser extends AbstractCommentParser {
private Javadoc docComment;
private AST ast;
DocCommentParser(AST ast, Scanner scanner, boolean check) {
super(null);
this.ast = ast;
this.scanner = scanner;
switch(this.ast.apiLevel()) {
case AST.JLS2_INTERNAL :
this.sourceLevel = ClassFileConstants.JDK1_3;
break;
case AST.JLS3_INTERNAL:
this.sourceLevel = ClassFileConstants.JDK1_5;
break;
default:
// AST.JLS4 for now
this.sourceLevel = ClassFileConstants.JDK1_7;
}
this.checkDocComment = check;
this.kind = DOM_PARSER | TEXT_PARSE;
}
/* (non-Javadoc)
* Returns true if tag @deprecated is present in annotation.
*
* If annotation checking is enabled, will also construct an Annotation node, which will be stored into Parser.annotation
* slot for being consumed later on.
*/
public Javadoc parse(int[] positions) {
return parse(positions[0], positions[1]-positions[0]);
}
public Javadoc parse(int start, int length) {
// Init
this.source = this.scanner.source;
this.lineEnds = this.scanner.lineEnds;
this.docComment = new Javadoc(this.ast);
// Parse
if (this.checkDocComment) {
this.javadocStart = start;
this.javadocEnd = start+length-1;
this.firstTagPosition = this.javadocStart;
commentParse();
}
this.docComment.setSourceRange(start, length);
if (this.ast.apiLevel == AST.JLS2_INTERNAL) {
setComment(start, length); // backward compatibility
}
return this.docComment;
}
/**
* Sets the comment starting at the given position and with the given length.
* <p>
* Note the only purpose of this method is to hide deprecated warnings.
* @deprecated mark deprecated to hide deprecated usage
*/
private void setComment(int start, int length) {
this.docComment.setComment(new String(this.source, start, length));
}
@Override
public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append("javadoc: ").append(this.docComment).append("\n"); //$NON-NLS-1$ //$NON-NLS-2$
buffer.append(super.toString());
return buffer.toString();
}
@Override
protected Object createArgumentReference(char[] name, int dim, boolean isVarargs, Object typeRef, long[] dimPositions, long argNamePos) throws InvalidInputException {
try {
MethodRefParameter argument = this.ast.newMethodRefParameter();
ASTNode node = (ASTNode) typeRef;
int argStart = node.getStartPosition();
int argEnd = node.getStartPosition()+node.getLength()-1;
if (dim > 0) argEnd = (int) dimPositions[dim-1];
if (argNamePos >= 0) argEnd = (int) argNamePos;
if (name.length != 0) {
final SimpleName argName = new SimpleName(this.ast);
argName.internalSetIdentifier(new String(name));
argument.setName(argName);
int argNameStart = (int) (argNamePos >>> 32);
argName.setSourceRange(argNameStart, argEnd-argNameStart+1);
}
Type argType = null;
if (node.getNodeType() == ASTNode.PRIMITIVE_TYPE) {
argType = (PrimitiveType) node;
} else {
Name argTypeName = (Name) node;
argType = this.ast.newSimpleType(argTypeName);
argType.setSourceRange(argStart, node.getLength());
}
if (dim > 0 && !isVarargs) {
if (this.ast.apiLevel <= AST.JLS4_INTERNAL) {
for (int i=0; i<dim; i++) {
argType = this.ast.newArrayType(argType);
argType.setSourceRange(argStart, ((int) dimPositions[i])-argStart+1);
}
} else {
ArrayType argArrayType = this.ast.newArrayType(argType, 0);
argType = argArrayType;
argType.setSourceRange(argStart, ((int) dimPositions[dim-1])-argStart+1);
for (int i=0; i<dim; i++) {
Dimension dimension = this.ast.newDimension();
int dimStart = (int) (dimPositions[i] >>> 32);
int dimEnd = (int) dimPositions[i];
dimension.setSourceRange(dimStart, dimEnd-dimStart+1);
argArrayType.dimensions().add(dimension);
}
}
}
argument.setType(argType);
if (this.ast.apiLevel > AST.JLS8_INTERNAL) {
argument.setVarargs(isVarargs);
}
argument.setSourceRange(argStart, argEnd - argStart + 1);
return argument;
}
catch (ClassCastException ex) {
throw new InvalidInputException();
}
}
@Override
protected Object createFieldReference(Object receiver) throws InvalidInputException {
try {
MemberRef fieldRef = this.ast.newMemberRef();
SimpleName fieldName = new SimpleName(this.ast);
fieldName.internalSetIdentifier(new String(this.identifierStack[0]));
fieldRef.setName(fieldName);
int start = (int) (this.identifierPositionStack[0] >>> 32);
int end = (int) this.identifierPositionStack[0];
fieldName.setSourceRange(start, end - start + 1);
if (receiver == null) {
start = this.memberStart;
fieldRef.setSourceRange(start, end - start + 1);
} else {
Name typeRef = (Name) receiver;
fieldRef.setQualifier(typeRef);
start = typeRef.getStartPosition();
end = fieldName.getStartPosition()+fieldName.getLength()-1;
fieldRef.setSourceRange(start, end-start+1);
}
return fieldRef;
}
catch (ClassCastException ex) {
throw new InvalidInputException();
}
}
@Override
protected Object createMethodReference(Object receiver, List arguments) throws InvalidInputException {
try {
// Create method ref
MethodRef methodRef = this.ast.newMethodRef();
SimpleName methodName = new SimpleName(this.ast);
int length = this.identifierLengthStack[0] - 1; // may be > 0 for member class constructor reference
methodName.internalSetIdentifier(new String(this.identifierStack[length]));
methodRef.setName(methodName);
int start = (int) (this.identifierPositionStack[length] >>> 32);
int end = (int) this.identifierPositionStack[length];
methodName.setSourceRange(start, end - start + 1);
// Set qualifier
if (receiver == null) {
start = this.memberStart;
methodRef.setSourceRange(start, end - start + 1);
} else {
Name typeRef = (Name) receiver;
methodRef.setQualifier(typeRef);
start = typeRef.getStartPosition();
}
// Add arguments
if (arguments != null) {
Iterator parameters = arguments.listIterator();
while (parameters.hasNext()) {
MethodRefParameter param = (MethodRefParameter) parameters.next();
methodRef.parameters().add(param);
}
}
methodRef.setSourceRange(start, this.scanner.getCurrentTokenEndPosition()-start+1);
return methodRef;
}
catch (ClassCastException ex) {
throw new InvalidInputException();
}
}
@Override
protected void createTag() {
TagElement tagElement = this.ast.newTagElement();
int position = this.scanner.currentPosition;
this.scanner.resetTo(this.tagSourceStart, this.tagSourceEnd);
StringBuffer tagName = new StringBuffer();
int start = this.tagSourceStart;
this.scanner.getNextChar();
while (this.scanner.currentPosition <= (this.tagSourceEnd+1)) {
tagName.append(this.scanner.currentCharacter);
this.scanner.getNextChar();
}
tagElement.setTagName(tagName.toString());
if (this.inlineTagStarted) {
start = this.inlineTagStart;
TagElement previousTag = null;
if (this.astPtr == -1) {
previousTag = this.ast.newTagElement();
previousTag.setSourceRange(start, this.tagSourceEnd-start+1);
pushOnAstStack(previousTag, true);
} else {
previousTag = (TagElement) this.astStack[this.astPtr];
}
int previousStart = previousTag.getStartPosition();
previousTag.fragments().add(tagElement);
previousTag.setSourceRange(previousStart, this.tagSourceEnd-previousStart+1);
} else {
pushOnAstStack(tagElement, true);
}
tagElement.setSourceRange(start, this.tagSourceEnd-start+1);
this.scanner.resetTo(position, this.javadocEnd);
}
@Override
protected Object createTypeReference(int primitiveToken) {
int size = this.identifierLengthStack[this.identifierLengthPtr];
String[] identifiers = new String[size];
int pos = this.identifierPtr - size + 1;
for (int i = 0; i < size; i++) {
identifiers[i] = new String(this.identifierStack[pos+i]);
}
ASTNode typeRef = null;
if (primitiveToken == -1) {
typeRef = this.ast.internalNewName(identifiers);
} else {
switch (primitiveToken) {
case TerminalTokens.TokenNamevoid :
typeRef = this.ast.newPrimitiveType(PrimitiveType.VOID);
break;
case TerminalTokens.TokenNameboolean :
typeRef = this.ast.newPrimitiveType(PrimitiveType.BOOLEAN);
break;
case TerminalTokens.TokenNamebyte :
typeRef = this.ast.newPrimitiveType(PrimitiveType.BYTE);
break;
case TerminalTokens.TokenNamechar :
typeRef = this.ast.newPrimitiveType(PrimitiveType.CHAR);
break;
case TerminalTokens.TokenNamedouble :
typeRef = this.ast.newPrimitiveType(PrimitiveType.DOUBLE);
break;
case TerminalTokens.TokenNamefloat :
typeRef = this.ast.newPrimitiveType(PrimitiveType.FLOAT);
break;
case TerminalTokens.TokenNameint :
typeRef = this.ast.newPrimitiveType(PrimitiveType.INT);
break;
case TerminalTokens.TokenNamelong :
typeRef = this.ast.newPrimitiveType(PrimitiveType.LONG);
break;
case TerminalTokens.TokenNameshort :
typeRef = this.ast.newPrimitiveType(PrimitiveType.SHORT);
break;
default:
// should not happen
return null;
}
}
// Update ref for whole name
int start = (int) (this.identifierPositionStack[pos] >>> 32);
// int end = (int) this.identifierPositionStack[this.identifierPtr];
// typeRef.setSourceRange(start, end-start+1);
// Update references of each simple name
if (size > 1) {
Name name = (Name)typeRef;
int nameIndex = size;
for (int i=this.identifierPtr; i>pos; i--, nameIndex--) {
int s = (int) (this.identifierPositionStack[i] >>> 32);
int e = (int) this.identifierPositionStack[i];
name.index = nameIndex;
SimpleName simpleName = ((QualifiedName)name).getName();
simpleName.index = nameIndex;
simpleName.setSourceRange(s, e-s+1);
name.setSourceRange(start, e-start+1);
name = ((QualifiedName)name).getQualifier();
}
int end = (int) this.identifierPositionStack[pos];
name.setSourceRange(start, end-start+1);
name.index = nameIndex;
} else {
int end = (int) this.identifierPositionStack[pos];
typeRef.setSourceRange(start, end-start+1);
}
return typeRef;
}
@Override
protected boolean parseIdentifierTag(boolean report) {
if (super.parseIdentifierTag(report)) {
createTag();
this.index = this.tagSourceEnd+1;
this.scanner.resetTo(this.index, this.javadocEnd);
return true;
}
return false;
}
/*
* Parse @return tag declaration
*/
protected boolean parseReturn() {
createTag();
return true;
}
@Override
protected boolean parseTag(int previousPosition) throws InvalidInputException {
// Read tag name
int currentPosition = this.index;
int token = readTokenAndConsume();
char[] tagName = CharOperation.NO_CHAR;
if (currentPosition == this.scanner.startPosition) {
this.tagSourceStart = this.scanner.getCurrentTokenStartPosition();
this.tagSourceEnd = this.scanner.getCurrentTokenEndPosition();
tagName = this.scanner.getCurrentIdentifierSource();
} else {
this.tagSourceEnd = currentPosition-1;
}
// Try to get tag name other than java identifier
// (see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=51660)
if (this.scanner.currentCharacter != ' ' && !ScannerHelper.isWhitespace(this.scanner.currentCharacter)) {
tagNameToken: while (token != TerminalTokens.TokenNameEOF && this.index < this.scanner.eofPosition) {
int length = tagName.length;
// !, ", #, %, &, ', -, :, <, >, * chars and spaces are not allowed in tag names
switch (this.scanner.currentCharacter) {
case '}':
case '*': // break for '*' as this is perhaps the end of comment (bug 65288)
case '!':
case '#':
case '%':
case '&':
case '\'':
case '"':
case ':':
case '<':
case '>':
break tagNameToken;
case '-': // allowed in tag names as this character is often used in doclets (bug 68087)
System.arraycopy(tagName, 0, tagName = new char[length+1], 0, length);
tagName[length] = this.scanner.currentCharacter;
break;
default:
if (this.scanner.currentCharacter == ' ' || ScannerHelper.isWhitespace(this.scanner.currentCharacter)) {
break tagNameToken;
}
token = readTokenAndConsume();
char[] ident = this.scanner.getCurrentIdentifierSource();
System.arraycopy(tagName, 0, tagName = new char[length+ident.length], 0, length);
System.arraycopy(ident, 0, tagName, length, ident.length);
break;
}
this.tagSourceEnd = this.scanner.getCurrentTokenEndPosition();
this.scanner.getNextChar();
this.index = this.scanner.currentPosition;
}
}
int length = tagName.length;
this.index = this.tagSourceEnd+1;
this.scanner.currentPosition = this.tagSourceEnd+1;
this.tagSourceStart = previousPosition;
// tage name may be empty (see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=125903)
if (tagName.length == 0) {
return false;
}
// Decide which parse to perform depending on tag name
this.tagValue = NO_TAG_VALUE;
boolean valid = true;
switch (token) {
case TerminalTokens.TokenNameIdentifier :
switch (tagName[0]) {
case 'c':
if (length == TAG_CATEGORY_LENGTH && CharOperation.equals(TAG_CATEGORY, tagName)) {
this.tagValue = TAG_CATEGORY_VALUE;
valid = parseIdentifierTag(false); // TODO (frederic) reconsider parameter value when @category will be significant in spec
} else if (length == TAG_CODE_LENGTH && CharOperation.equals(TAG_CODE, tagName)) {
this.tagValue = TAG_CODE_VALUE;
createTag();
} else {
this.tagValue = TAG_OTHERS_VALUE;
createTag();
}
break;
case 'd':
if (length == TAG_DEPRECATED_LENGTH && CharOperation.equals(TAG_DEPRECATED, tagName)) {
this.deprecated = true;
this.tagValue = TAG_DEPRECATED_VALUE;
} else {
this.tagValue = TAG_OTHERS_VALUE;
}
createTag();
break;
case 'i':
if (length == TAG_INHERITDOC_LENGTH && CharOperation.equals(TAG_INHERITDOC, tagName)) {
if (this.reportProblems) {
recordInheritedPosition((((long) this.tagSourceStart) << 32) + this.tagSourceEnd);
}
this.tagValue = TAG_INHERITDOC_VALUE;
} else {
this.tagValue = TAG_OTHERS_VALUE;
}
createTag();
break;
case 'p':
if (length == TAG_PARAM_LENGTH && CharOperation.equals(TAG_PARAM, tagName)) {
this.tagValue = TAG_PARAM_VALUE;
valid = parseParam();
} else {
this.tagValue = TAG_OTHERS_VALUE;
createTag();
}
break;
case 'e':
if (length == TAG_EXCEPTION_LENGTH && CharOperation.equals(TAG_EXCEPTION, tagName)) {
this.tagValue = TAG_EXCEPTION_VALUE;
valid = parseThrows();
} else {
this.tagValue = TAG_OTHERS_VALUE;
createTag();
}
break;
case 's':
if (length == TAG_SEE_LENGTH && CharOperation.equals(TAG_SEE, tagName)) {
this.tagValue = TAG_SEE_VALUE;
if (this.inlineTagStarted) {
// bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=53290
// Cannot have @see inside inline comment
valid = false;
} else {
valid = parseReference();
}
} else {
this.tagValue = TAG_OTHERS_VALUE;
createTag();
}
break;
case 'l':
if (length == TAG_LINK_LENGTH && CharOperation.equals(TAG_LINK, tagName)) {
this.tagValue = TAG_LINK_VALUE;
} else if (length == TAG_LINKPLAIN_LENGTH && CharOperation.equals(TAG_LINKPLAIN, tagName)) {
this.tagValue = TAG_LINKPLAIN_VALUE;
} else if (length == TAG_LITERAL_LENGTH && CharOperation.equals(TAG_LITERAL, tagName)) {
this.tagValue = TAG_LITERAL_VALUE;
}
if (this.tagValue != NO_TAG_VALUE && this.tagValue != TAG_LITERAL_VALUE) {
if (this.inlineTagStarted) {
valid = parseReference();
} else {
// bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=53290
// Cannot have @link outside inline comment
valid = false;
}
} else {
if (this.tagValue == NO_TAG_VALUE) this.tagValue = TAG_OTHERS_VALUE;
createTag();
}
break;
case 'v':
if (this.sourceLevel >= ClassFileConstants.JDK1_5 && length == TAG_VALUE_LENGTH && CharOperation.equals(TAG_VALUE, tagName)) {
this.tagValue = TAG_VALUE_VALUE;
if (this.inlineTagStarted) {
valid = parseReference();
} else {
valid = false;
}
} else {
this.tagValue = TAG_OTHERS_VALUE;
createTag();
}
break;
default:
this.tagValue = TAG_OTHERS_VALUE;
createTag();
}
break;
case TerminalTokens.TokenNamereturn :
this.tagValue = TAG_RETURN_VALUE;
valid = parseReturn();
break;
case TerminalTokens.TokenNamethrows :
this.tagValue = TAG_THROWS_VALUE;
valid = parseThrows();
break;
case TerminalTokens.TokenNameabstract:
case TerminalTokens.TokenNameassert:
case TerminalTokens.TokenNameboolean:
case TerminalTokens.TokenNamebreak:
case TerminalTokens.TokenNamebyte:
case TerminalTokens.TokenNamecase:
case TerminalTokens.TokenNamecatch:
case TerminalTokens.TokenNamechar:
case TerminalTokens.TokenNameclass:
case TerminalTokens.TokenNamecontinue:
case TerminalTokens.TokenNamedefault:
case TerminalTokens.TokenNamedo:
case TerminalTokens.TokenNamedouble:
case TerminalTokens.TokenNameelse:
case TerminalTokens.TokenNameextends:
case TerminalTokens.TokenNamefalse:
case TerminalTokens.TokenNamefinal:
case TerminalTokens.TokenNamefinally:
case TerminalTokens.TokenNamefloat:
case TerminalTokens.TokenNamefor:
case TerminalTokens.TokenNameif:
case TerminalTokens.TokenNameimplements:
case TerminalTokens.TokenNameimport:
case TerminalTokens.TokenNameinstanceof:
case TerminalTokens.TokenNameint:
case TerminalTokens.TokenNameinterface:
case TerminalTokens.TokenNamelong:
case TerminalTokens.TokenNamenative:
case TerminalTokens.TokenNamenew:
case TerminalTokens.TokenNamenull:
case TerminalTokens.TokenNamepackage:
case TerminalTokens.TokenNameprivate:
case TerminalTokens.TokenNameprotected:
case TerminalTokens.TokenNamepublic:
case TerminalTokens.TokenNameshort:
case TerminalTokens.TokenNamestatic:
case TerminalTokens.TokenNamestrictfp:
case TerminalTokens.TokenNamesuper:
case TerminalTokens.TokenNameswitch:
case TerminalTokens.TokenNamesynchronized:
case TerminalTokens.TokenNamethis:
case TerminalTokens.TokenNamethrow:
case TerminalTokens.TokenNametransient:
case TerminalTokens.TokenNametrue:
case TerminalTokens.TokenNametry:
case TerminalTokens.TokenNamevoid:
case TerminalTokens.TokenNamevolatile:
case TerminalTokens.TokenNamewhile:
case TerminalTokens.TokenNameenum :
case TerminalTokens.TokenNameconst :
case TerminalTokens.TokenNamegoto :
this.tagValue = TAG_OTHERS_VALUE;
createTag();
break;
}
this.textStart = this.index;
return valid;
}
@Override
protected boolean pushParamName(boolean isTypeParam) {
int idIndex = isTypeParam ? 1 : 0;
final SimpleName name = new SimpleName(this.ast);
name.internalSetIdentifier(new String(this.identifierStack[idIndex]));
int nameStart = (int) (this.identifierPositionStack[idIndex] >>> 32);
int nameEnd = (int) (this.identifierPositionStack[idIndex] & 0x00000000FFFFFFFFL);
name.setSourceRange(nameStart, nameEnd-nameStart+1);
TagElement paramTag = this.ast.newTagElement();
paramTag.setTagName(TagElement.TAG_PARAM);
if (isTypeParam) { // specific storage for @param <E> (see bug 79809)
// '<' was stored in identifiers stack
TextElement text = this.ast.newTextElement();
text.setText(new String(this.identifierStack[0]));
int txtStart = (int) (this.identifierPositionStack[0] >>> 32);
int txtEnd = (int) (this.identifierPositionStack[0] & 0x00000000FFFFFFFFL);
text.setSourceRange(txtStart, txtEnd-txtStart+1);
paramTag.fragments().add(text);
// add simple name
paramTag.fragments().add(name);
// '>' was stored in identifiers stack
text = this.ast.newTextElement();
text.setText(new String(this.identifierStack[2]));
txtStart = (int) (this.identifierPositionStack[2] >>> 32);
txtEnd = (int) (this.identifierPositionStack[2] & 0x00000000FFFFFFFFL);
text.setSourceRange(txtStart, txtEnd-txtStart+1);
paramTag.fragments().add(text);
// set param tag source range
paramTag.setSourceRange(this.tagSourceStart, txtEnd-this.tagSourceStart+1);
} else {
paramTag.setSourceRange(this.tagSourceStart, nameEnd-this.tagSourceStart+1);
paramTag.fragments().add(name);
}
pushOnAstStack(paramTag, true);
return true;
}
@Override
protected boolean pushSeeRef(Object statement) {
TagElement seeTag = this.ast.newTagElement();
ASTNode node = (ASTNode) statement;
seeTag.fragments().add(node);
int end = node.getStartPosition()+node.getLength()-1;
if (this.inlineTagStarted) {
seeTag.setSourceRange(this.inlineTagStart, end-this.inlineTagStart+1);
switch (this.tagValue) {
case TAG_LINK_VALUE:
seeTag.setTagName(TagElement.TAG_LINK);
break;
case TAG_LINKPLAIN_VALUE:
seeTag.setTagName(TagElement.TAG_LINKPLAIN);
break;
case TAG_VALUE_VALUE:
seeTag.setTagName(TagElement.TAG_VALUE);
break;
}
TagElement previousTag = null;
int previousStart = this.inlineTagStart;
if (this.astPtr == -1) {
previousTag = this.ast.newTagElement();
pushOnAstStack(previousTag, true);
} else {
previousTag = (TagElement) this.astStack[this.astPtr];
previousStart = previousTag.getStartPosition();
}
previousTag.fragments().add(seeTag);
previousTag.setSourceRange(previousStart, end-previousStart+1);
} else {
seeTag.setTagName(TagElement.TAG_SEE);
seeTag.setSourceRange(this.tagSourceStart, end-this.tagSourceStart+1);
pushOnAstStack(seeTag, true);
}
return true;
}
@Override
protected void pushText(int start, int end) {
// Create text element
TextElement text = this.ast.newTextElement();
text.setText(new String( this.source, start, end-start));
text.setSourceRange(start, end-start);
// Search previous tag on which to add the text element
TagElement previousTag = null;
int previousStart = start;
if (this.astPtr == -1) {
previousTag = this.ast.newTagElement();
previousTag.setSourceRange(start, end-start);
pushOnAstStack(previousTag, true);
} else {
previousTag = (TagElement) this.astStack[this.astPtr];
previousStart = previousTag.getStartPosition();
}
// If we're in a inline tag, then retrieve previous tag in its fragments
List fragments = previousTag.fragments();
if (this.inlineTagStarted) {
int size = fragments.size();
if (size == 0) {
// no existing fragment => just add the element
TagElement inlineTag = this.ast.newTagElement();
fragments.add(inlineTag);
previousTag = inlineTag;
} else {
// If last fragment is a tag, then use it as previous tag
ASTNode lastFragment = (ASTNode) fragments.get(size-1);
if (lastFragment.getNodeType() == ASTNode.TAG_ELEMENT) {
previousTag = (TagElement) lastFragment;
previousStart = previousTag.getStartPosition();
}
}
}
// Add the text
previousTag.fragments().add(text);
previousTag.setSourceRange(previousStart, end-previousStart);
this.textStart = -1;
}
@Override
protected boolean pushThrowName(Object typeRef) {
TagElement throwsTag = this.ast.newTagElement();
switch (this.tagValue) {
case TAG_THROWS_VALUE:
throwsTag.setTagName(TagElement.TAG_THROWS);
break;
case TAG_EXCEPTION_VALUE:
throwsTag.setTagName(TagElement.TAG_EXCEPTION);
break;
}
throwsTag.setSourceRange(this.tagSourceStart, this.scanner.getCurrentTokenEndPosition()-this.tagSourceStart+1);
throwsTag.fragments().add(typeRef);
pushOnAstStack(throwsTag, true);
return true;
}
@Override
protected void refreshInlineTagPosition(int previousPosition) {
if (this.astPtr != -1) {
TagElement previousTag = (TagElement) this.astStack[this.astPtr];
if (this.inlineTagStarted) {
int previousStart = previousTag.getStartPosition();
previousTag.setSourceRange(previousStart, previousPosition-previousStart+1);
if (previousTag.fragments().size() > 0) {
ASTNode inlineTag = (ASTNode) previousTag.fragments().get(previousTag.fragments().size()-1);
if (inlineTag.getNodeType() == ASTNode.TAG_ELEMENT) {
int inlineStart = inlineTag.getStartPosition();
inlineTag.setSourceRange(inlineStart, previousPosition-inlineStart+1);
}
}
}
}
}
/*
* Add stored tag elements to associated comment.
*/
@Override
protected void updateDocComment() {
for (int idx = 0; idx <= this.astPtr; idx++) {
this.docComment.tags().add(this.astStack[idx]);
}
}
}