/******************************************************************************* * Copyright (c) 2004, 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.core.dom; import java.util.Iterator; import java.util.List; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.core.compiler.InvalidInputException; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser; import org.eclipse.jdt.internal.compiler.parser.Scanner; import org.eclipse.jdt.internal.compiler.parser.ScannerHelper; import org.eclipse.jdt.internal.compiler.parser.TerminalTokens; /** * Internal parser used for decoding doc comments. * * @since 3.0 */ 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; this.sourceLevel= this.ast.apiLevel() >= AST.JLS3 ? ClassFileConstants.JDK1_5 : ClassFileConstants.JDK1_3; 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)); } 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(); } /* (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 { 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; // if (dim > 0) { // argType = this.ast.newArrayType(argType, dim); // argType.setSourceRange(argStart, ((int) dimPositions[dim-1])-argStart+1); // } } else { Name argTypeName= (Name)node; argType= this.ast.newSimpleType(argTypeName); argType.setSourceRange(argStart, node.getLength()); } if (dim > 0 && !isVarargs) { for (int i= 0; i < dim; i++) { argType= this.ast.newArrayType(argType); argType.setSourceRange(argStart, ((int)dimPositions[i]) - argStart + 1); } } argument.setType(argType); argument.setSourceRange(argStart, argEnd - argStart + 1); return argument; } 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 { 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(); } } /* (non-Javadoc) * @see org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser#createMethodReference(java.lang.Object[]) */ 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(); } } /* (non-Javadoc) * @see org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser#createTag() */ 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); } /* (non-Javadoc) * @see org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser#createTypeReference() */ 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; } /* (non-Javadoc) * @see org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser#parseIdentifierTag(boolean) */ 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; } /* (non-Javadoc) * @see org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser#parseTag(int) */ 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 { 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; } if (this.tagValue != NO_TAG_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 { 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; } /* (non-Javadoc) * @see org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser#pushParamName(java.lang.Object) */ 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; } /* (non-Javadoc) * @see org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser#pushSeeRef(java.lang.Object) */ 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; } /* (non-Javadoc) * @see org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser#pushText(int, int) */ 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; } /* (non-Javadoc) * @see org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser#pushThrowName(java.lang.Object) */ 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; } /* (non-Javadoc) * @see org.eclipse.jdt.internal.compiler.parser.AbstractCommentParser#refreshInlineTagPosition(int) */ 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. */ protected void updateDocComment() { for (int idx= 0; idx <= this.astPtr; idx++) { this.docComment.tags().add(this.astStack[idx]); } } }