2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com
.intellij
.psi
.impl
.source
.parsing
;
18 import com
.intellij
.lang
.ASTFactory
;
19 import com
.intellij
.lang
.ASTNode
;
20 import com
.intellij
.lexer
.FilterLexer
;
21 import com
.intellij
.lexer
.JavaDocLexer
;
22 import com
.intellij
.lexer
.Lexer
;
23 import com
.intellij
.openapi
.diagnostic
.Logger
;
24 import com
.intellij
.openapi
.roots
.LanguageLevelProjectExtension
;
25 import com
.intellij
.pom
.java
.LanguageLevel
;
26 import com
.intellij
.psi
.JavaDocTokenType
;
27 import com
.intellij
.psi
.PsiManager
;
28 import com
.intellij
.psi
.TokenType
;
29 import com
.intellij
.psi
.impl
.source
.DummyHolderFactory
;
30 import com
.intellij
.psi
.impl
.source
.ParsingContext
;
31 import com
.intellij
.psi
.impl
.source
.tree
.*;
32 import com
.intellij
.psi
.tree
.IElementType
;
33 import com
.intellij
.psi
.tree
.TokenSet
;
34 import org
.jetbrains
.annotations
.NonNls
;
36 public class JavadocParsing
extends Parsing
{
37 private static final Logger LOG
= Logger
.getInstance("#com.intellij.psi.impl.source.parsing.JavadocParsing");
39 private static final TokenSet TOKEN_FILTER
= TokenSet
.create(JavaDocTokenType
.DOC_SPACE
, JavaDocTokenType
.DOC_COMMENT_LEADING_ASTERISKS
);
41 private static final TokenSet TAG_VALUE
= TokenSet
.create(JavaDocTokenType
.DOC_TAG_VALUE_TOKEN
, JavaDocTokenType
.DOC_TAG_VALUE_COMMA
,
42 JavaDocTokenType
.DOC_TAG_VALUE_DOT
, JavaDocTokenType
.DOC_TAG_VALUE_LPAREN
,
43 JavaDocTokenType
.DOC_TAG_VALUE_RPAREN
, JavaDocTokenType
.DOC_TAG_VALUE_SHARP_TOKEN
,
44 JavaDocTokenType
.DOC_TAG_VALUE_LT
, JavaDocTokenType
.DOC_TAG_VALUE_GT
);
46 private int myBraceScope
= 0;
47 @NonNls private static final String SEE_TAG
= "@see";
48 @NonNls private static final String LINK_TAG
= "@link";
49 @NonNls private static final String LINKPLAIN_TAG
= "@linkplain";
50 @NonNls private static final String THROWS_TAG
= "@throws";
51 @NonNls private static final String EXCEPTION_TAG
= "@exception";
52 @NonNls private static final String PARAM_TAG
= "@param";
53 @NonNls private static final String VALUE_TAG
= "@value";
55 public JavadocParsing(JavaParsingContext context
) {
59 public TreeElement
parseJavaDocReference(CharSequence myBuffer
, Lexer originalLexer
, boolean isType
, PsiManager manager
) {
60 FilterLexer lexer
= new FilterLexer(originalLexer
, new FilterLexer
.SetFilter(StdTokenSets
.WHITE_SPACE_OR_COMMENT_BIT_SET
));
61 lexer
.start(myBuffer
);
63 final FileElement dummyRoot
= DummyHolderFactory
.createHolder(manager
, null, myContext
.getCharTable()).getTreeElement();
64 final CompositeElement element
;
67 element
= parseTypeWithEllipsis(lexer
, true, false);
70 element
= myContext
.getStatementParsing().parseJavaCodeReference(lexer
, true, true, false);
74 dummyRoot
.rawAddChildren(element
);
76 while(lexer
.getTokenType() != null){
77 dummyRoot
.rawAddChildren(ParseUtil
.createTokenElement(lexer
, myContext
.getCharTable()));
81 ParseUtil
.insertMissingTokens(dummyRoot
, originalLexer
, 0, myBuffer
.length(), 0, WhiteSpaceAndCommentsProcessor
.INSTANCE
, myContext
);
82 return dummyRoot
.getFirstChildNode();
85 public TreeElement
parseDocCommentText(PsiManager manager
, CharSequence buffer
, int startOffset
, int endOffset
) {
86 Lexer originalLexer
= new JavaDocLexer(LanguageLevelProjectExtension
.getInstance(manager
.getProject()).getLanguageLevel().hasEnumKeywordAndAutoboxing()); // we need caching lexer because the lexer has states
88 FilterLexer lexer
= new FilterLexer(originalLexer
, new FilterLexer
.SetFilter(TOKEN_FILTER
));
89 lexer
.start(buffer
, startOffset
, endOffset
);
90 final FileElement dummyRoot
= DummyHolderFactory
.createHolder(manager
, null, myContext
.getCharTable()).getTreeElement();
93 IElementType tokenType
= lexer
.getTokenType();
94 if (tokenType
== null) break;
95 if (tokenType
== JavaDocTokenType
.DOC_TAG_NAME
) {
96 CompositeElement tag
= parseTag(manager
, lexer
);
97 dummyRoot
.rawAddChildren(tag
);
100 TreeElement element
= parseDataItem(manager
, lexer
, null, false);
101 dummyRoot
.rawAddChildren(element
);
105 ParseUtil
.insertMissingTokens(dummyRoot
, originalLexer
, startOffset
, endOffset
, -1, new JDTokenProcessor(this), myContext
);
106 return dummyRoot
.getFirstChildNode();
109 private CompositeElement
parseTag(PsiManager manager
, Lexer lexer
) {
110 if (lexer
.getTokenType() != JavaDocTokenType
.DOC_TAG_NAME
) return null;
111 CompositeElement tag
= ASTFactory
.composite(JavaDocElementType
.DOC_TAG
);
112 tag
.rawAddChildren(createTokenElement(lexer
));
113 String tagName
= lexer
.getBufferSequence().subSequence(lexer
.getTokenStart(), lexer
.getTokenEnd()).toString();
116 IElementType tokenType
= lexer
.getTokenType();
117 if (tokenType
== null || tokenType
== JavaDocTokenType
.DOC_TAG_NAME
|| tokenType
== JavaDocTokenType
.DOC_COMMENT_END
) break;
118 TreeElement element
= parseDataItem(manager
, lexer
, tagName
, false);
119 tag
.rawAddChildren(element
);
124 private TreeElement
parseDataItem(PsiManager manager
, Lexer lexer
, String tagName
, boolean isInlineItem
) {
125 if (lexer
.getTokenType() == JavaDocTokenType
.DOC_INLINE_TAG_START
) {
126 LeafElement justABrace
= ASTFactory
.leaf(JavaDocTokenType
.DOC_COMMENT_DATA
, myContext
.tokenText(lexer
));
127 CompositeElement tag
= ASTFactory
.composite(JavaDocElementType
.DOC_INLINE_TAG
);
128 final LeafElement leafElement
= ASTFactory
.leaf(JavaDocTokenType
.DOC_INLINE_TAG_START
, myContext
.tokenText(lexer
));
129 tag
.rawAddChildren(leafElement
);
133 if (myBraceScope
> 0) {
138 if (lexer
.getTokenType() != JavaDocTokenType
.DOC_TAG_NAME
&&
139 lexer
.getTokenType() != JavaDocTokenType
.DOC_COMMENT_BAD_CHARACTER
) {
145 String inlineTagName
= "";
148 IElementType tokenType
= lexer
.getTokenType();
149 if (tokenType
== JavaDocTokenType
.DOC_TAG_NAME
) {
150 inlineTagName
= lexer
.getBufferSequence().subSequence(lexer
.getTokenStart(), lexer
.getTokenEnd()).toString();
153 if (tokenType
== null || tokenType
== JavaDocTokenType
.DOC_COMMENT_END
) break;
154 TreeElement element
= parseDataItem(manager
, lexer
, inlineTagName
, true);
155 tag
.rawAddChildren(element
);
156 if (tokenType
== JavaDocTokenType
.DOC_INLINE_TAG_END
) {
157 if (myBraceScope
> 0) myBraceScope
--;
158 if (myBraceScope
== 0) break;
163 else if (TAG_VALUE
.contains(lexer
.getTokenType())) {
164 if (SEE_TAG
.equals(tagName
) && !isInlineItem
) {
165 return parseSeeTagValue(lexer
);
167 else if (LINK_TAG
.equals(tagName
) && isInlineItem
) {
168 return parseSeeTagValue(lexer
);
171 if (LanguageLevelProjectExtension
.getInstance(manager
.getProject()).getLanguageLevel().compareTo(LanguageLevel
.JDK_1_4
) >= 0 &&
172 LINKPLAIN_TAG
.equals(tagName
) && isInlineItem
) {
173 return parseSeeTagValue(lexer
);
175 else if (!isInlineItem
&& (THROWS_TAG
.equals(tagName
) || EXCEPTION_TAG
.equals(tagName
))) {
176 final TreeElement element
= parseReferenceOrType(lexer
, false);
178 final CompositeElement tagValue
= ASTFactory
.composite(JavaDocTokenType
.DOC_TAG_VALUE_TOKEN
);
179 tagValue
.rawAddChildren(element
);
182 else if (!isInlineItem
&& tagName
!= null && tagName
.equals(PARAM_TAG
)) {
183 return parseParamTagValue(lexer
);
186 if (LanguageLevelProjectExtension
.getInstance(manager
.getProject()).getLanguageLevel().compareTo(LanguageLevel
.JDK_1_5
) >= 0 && VALUE_TAG
.equals(tagName
) && isInlineItem
) {
187 return parseSeeTagValue(lexer
);
190 return parseSimpleTagValue(lexer
);
196 TreeElement token
= createTokenElement(lexer
);
202 private TreeElement
parseParamTagValue(Lexer lexer
) {
203 CompositeElement tagValue
= ASTFactory
.composite(JavaDocElementType
.DOC_PARAMETER_REF
);
205 while (TAG_VALUE
.contains(lexer
.getTokenType())) {
206 TreeElement value
= createTokenElement(lexer
);
208 tagValue
.rawAddChildren(value
);
214 private TreeElement
parseSimpleTagValue(Lexer lexer
) {
215 CompositeElement tagValue
= ASTFactory
.composite(JavaDocTokenType
.DOC_TAG_VALUE_TOKEN
);
217 while (TAG_VALUE
.contains(lexer
.getTokenType())) {
218 TreeElement value
= createTokenElement(lexer
);
220 tagValue
.rawAddChildren(value
);
226 private ASTNode
parseMethodRef(Lexer lexer
) {
227 CompositeElement ref
= ASTFactory
.composite(JavaDocElementType
.DOC_METHOD_OR_FIELD_REF
);
229 TreeElement sharp
= createTokenElement(lexer
);
230 ref
.rawAddChildren(sharp
);
233 if (lexer
.getTokenType() != JavaDocTokenType
.DOC_TAG_VALUE_TOKEN
) return ref
;
234 TreeElement value
= createTokenElement(lexer
);
235 ref
.rawAddChildren(value
);
238 if (lexer
.getTokenType() == JavaDocTokenType
.DOC_TAG_VALUE_LPAREN
) {
239 TreeElement lparen
= createTokenElement(lexer
);
241 ref
.rawAddChildren(lparen
);
243 CompositeElement subValue
= ASTFactory
.composite(JavaDocTokenType
.DOC_TAG_VALUE_TOKEN
);
244 ref
.rawAddChildren(subValue
);
246 while (TAG_VALUE
.contains(lexer
.getTokenType())) {
247 if (lexer
.getTokenType() == JavaDocTokenType
.DOC_TAG_VALUE_TOKEN
) {
248 final TreeElement reference
= parseReferenceOrType(lexer
, true);
250 subValue
.rawAddChildren(reference
);
252 while (TAG_VALUE
.contains(lexer
.getTokenType()) && lexer
.getTokenType() != JavaDocTokenType
.DOC_TAG_VALUE_COMMA
&&
253 lexer
.getTokenType() != JavaDocTokenType
.DOC_TAG_VALUE_RPAREN
) {
254 final TreeElement tokenElement
= createTokenElement(lexer
);
256 subValue
.rawAddChildren(tokenElement
);
259 else if (lexer
.getTokenType() == JavaDocTokenType
.DOC_TAG_VALUE_RPAREN
) {
260 TreeElement rparen
= createTokenElement(lexer
);
262 ref
.rawAddChildren(rparen
);
266 final TreeElement tokenElement
= createTokenElement(lexer
);
268 subValue
.rawAddChildren(tokenElement
);
276 private TreeElement
parseSeeTagValue(Lexer lexer
) {
277 if (!TAG_VALUE
.contains(lexer
.getTokenType())) return null;
279 if (lexer
.getTokenType() == JavaDocTokenType
.DOC_TAG_VALUE_SHARP_TOKEN
) {
280 return (TreeElement
)parseMethodRef(lexer
);
282 else if (lexer
.getTokenType() == JavaDocTokenType
.DOC_TAG_VALUE_TOKEN
) {
283 final TreeElement element
= parseReferenceOrType(lexer
, false);
286 if (lexer
.getTokenType() == JavaDocTokenType
.DOC_TAG_VALUE_SHARP_TOKEN
) {
287 ASTNode methodRef
= parseMethodRef(lexer
);
288 ((TreeElement
)methodRef
.getFirstChildNode()).rawInsertBeforeMe(element
);
289 return (TreeElement
)methodRef
;
296 CompositeElement tagValue
= ASTFactory
.composite(JavaDocTokenType
.DOC_TAG_VALUE_TOKEN
);
297 TreeElement element
= createTokenElement(lexer
);
299 tagValue
.rawAddChildren(element
);
304 private TreeElement
parseReferenceOrType(Lexer lexer
, boolean isType
) {
305 return ASTFactory
.lazy(isType ? JavaDocElementType
.DOC_TYPE_HOLDER
: JavaDocElementType
.DOC_REFERENCE_HOLDER
, myContext
.tokenText(lexer
));
308 private LeafElement
createTokenElement(Lexer lexer
) {
309 IElementType tokenType
= lexer
.getTokenType();
310 if (tokenType
== JavaDocTokenType
.DOC_SPACE
) {
311 tokenType
= TokenType
.WHITE_SPACE
;
313 else if ((tokenType
== JavaDocTokenType
.DOC_INLINE_TAG_START
|| tokenType
== JavaDocTokenType
.DOC_INLINE_TAG_END
) && myBraceScope
!= 1) {
314 tokenType
= JavaDocTokenType
.DOC_COMMENT_DATA
;
317 return ASTFactory
.leaf(tokenType
, myContext
.tokenText(lexer
));
320 private static class JDTokenProcessor
implements TokenProcessor
{
321 private final JavadocParsing myParsing
;
323 private JDTokenProcessor(JavadocParsing theParsing
) {
324 myParsing
= theParsing
;
327 public TreeElement
process(Lexer lexer
, ParsingContext context
) {
328 TreeElement first
= null;
329 TreeElement last
= null;
330 while (isTokenValid(lexer
.getTokenType())) {
331 LeafElement tokenElement
= myParsing
.createTokenElement(lexer
);
332 IElementType type
= lexer
.getTokenType();
333 if (!TOKEN_FILTER
.contains(type
)) {
334 LOG
.assertTrue(false, "Missed token should be space or asterisks:" + tokenElement
);
335 throw new RuntimeException();
338 last
.setTreeNext(tokenElement
);
339 tokenElement
.setTreePrev(last
);
343 first
= last
= tokenElement
;
350 public boolean isTokenValid(IElementType tokenType
) {
351 return tokenType
!= null && TOKEN_FILTER
.contains(tokenType
);