update copyright
[fedora-idea.git] / java / java-impl / src / com / intellij / psi / impl / source / parsing / JavadocParsing.java
blobd5439d0dce6fccdad7e13675c66de6663ba23fb4
1 /*
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) {
56 super(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;
66 if (isType){
67 element = parseTypeWithEllipsis(lexer, true, false);
69 else{
70 element = myContext.getStatementParsing().parseJavaCodeReference(lexer, true, true, false);
73 if (element != null){
74 dummyRoot.rawAddChildren(element);
76 while(lexer.getTokenType() != null){
77 dummyRoot.rawAddChildren(ParseUtil.createTokenElement(lexer, myContext.getCharTable()));
78 lexer.advance();
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();
92 while (true) {
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);
99 else {
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();
114 lexer.advance();
115 while (true) {
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);
121 return tag;
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);
131 lexer.advance();
133 if (myBraceScope > 0) {
134 myBraceScope++;
135 return justABrace;
138 if (lexer.getTokenType() != JavaDocTokenType.DOC_TAG_NAME &&
139 lexer.getTokenType() != JavaDocTokenType.DOC_COMMENT_BAD_CHARACTER) {
140 return justABrace;
143 myBraceScope++;
145 String inlineTagName = "";
147 while (true) {
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;
161 return tag;
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);
170 else {
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);
177 lexer.advance();
178 final CompositeElement tagValue = ASTFactory.composite(JavaDocTokenType.DOC_TAG_VALUE_TOKEN);
179 tagValue.rawAddChildren(element);
180 return tagValue;
182 else if (!isInlineItem && tagName != null && tagName.equals(PARAM_TAG)) {
183 return parseParamTagValue(lexer);
185 else {
186 if (LanguageLevelProjectExtension.getInstance(manager.getProject()).getLanguageLevel().compareTo(LanguageLevel.JDK_1_5) >= 0 && VALUE_TAG.equals(tagName) && isInlineItem) {
187 return parseSeeTagValue(lexer);
189 else {
190 return parseSimpleTagValue(lexer);
195 else {
196 TreeElement token = createTokenElement(lexer);
197 lexer.advance();
198 return token;
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);
207 lexer.advance();
208 tagValue.rawAddChildren(value);
211 return tagValue;
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);
219 lexer.advance();
220 tagValue.rawAddChildren(value);
223 return tagValue;
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);
231 lexer.advance();
233 if (lexer.getTokenType() != JavaDocTokenType.DOC_TAG_VALUE_TOKEN) return ref;
234 TreeElement value = createTokenElement(lexer);
235 ref.rawAddChildren(value);
236 lexer.advance();
238 if (lexer.getTokenType() == JavaDocTokenType.DOC_TAG_VALUE_LPAREN) {
239 TreeElement lparen = createTokenElement(lexer);
240 lexer.advance();
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);
249 lexer.advance();
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);
255 lexer.advance();
256 subValue.rawAddChildren(tokenElement);
259 else if (lexer.getTokenType() == JavaDocTokenType.DOC_TAG_VALUE_RPAREN) {
260 TreeElement rparen = createTokenElement(lexer);
261 lexer.advance();
262 ref.rawAddChildren(rparen);
263 return ref;
265 else {
266 final TreeElement tokenElement = createTokenElement(lexer);
267 lexer.advance();
268 subValue.rawAddChildren(tokenElement);
273 return ref;
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);
284 lexer.advance();
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;
291 else {
292 return element;
295 else {
296 CompositeElement tagValue = ASTFactory.composite(JavaDocTokenType.DOC_TAG_VALUE_TOKEN);
297 TreeElement element = createTokenElement(lexer);
298 lexer.advance();
299 tagValue.rawAddChildren(element);
300 return tagValue;
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();
337 if (last != null) {
338 last.setTreeNext(tokenElement);
339 tokenElement.setTreePrev(last);
340 last = tokenElement;
342 else {
343 first = last = tokenElement;
345 lexer.advance();
347 return first;
350 public boolean isTokenValid(IElementType tokenType) {
351 return tokenType != null && TOKEN_FILTER.contains(tokenType);