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
.codeStyle
.javadoc
;
18 import com
.intellij
.lang
.ASTNode
;
19 import com
.intellij
.openapi
.diagnostic
.Logger
;
20 import com
.intellij
.openapi
.project
.Project
;
21 import com
.intellij
.openapi
.util
.text
.LineTokenizer
;
22 import com
.intellij
.psi
.*;
23 import com
.intellij
.psi
.codeStyle
.CodeStyleSettings
;
24 import com
.intellij
.psi
.codeStyle
.CodeStyleSettingsManager
;
25 import com
.intellij
.psi
.impl
.source
.SourceTreeToPsiMap
;
26 import com
.intellij
.psi
.javadoc
.PsiDocComment
;
27 import com
.intellij
.util
.IncorrectOperationException
;
28 import org
.jetbrains
.annotations
.Nullable
;
33 public class CommentFormatter
{
34 private static final Logger LOG
= Logger
.getInstance(
35 "#com.intellij.psi.impl.source.codeStyle.javadoc.CommentFormatter");
37 private final CodeStyleSettings mySettings
;
38 private final JDParser myParser
;
39 private final Project myProject
;
41 public CommentFormatter(Project project
) {
42 mySettings
= CodeStyleSettingsManager
.getSettings(project
);
43 myParser
= new JDParser(mySettings
);
47 public CodeStyleSettings
getSettings() {
51 public JDParser
getParser() {
55 public void process(ASTNode element
) {
56 if (!getSettings().ENABLE_JAVADOC_FORMATTING
) return;
58 PsiElement psiElement
= SourceTreeToPsiMap
.treeElementToPsi(element
);
59 processElementComment(psiElement
);
62 private void processElementComment(PsiElement psiElement
) {
63 if (psiElement
instanceof PsiClass
) {
64 String newCommentText
= formatClassComment((PsiClass
)psiElement
);
65 replaceDocComment(newCommentText
, (PsiDocCommentOwner
)psiElement
);
67 else if (psiElement
instanceof PsiMethod
) {
68 String newCommentText
= formatMethodComment((PsiMethod
)psiElement
);
69 replaceDocComment(newCommentText
, (PsiDocCommentOwner
)psiElement
);
71 else if (psiElement
instanceof PsiField
) {
72 String newCommentText
= formatFieldComment((PsiField
)psiElement
);
73 replaceDocComment(newCommentText
, (PsiDocCommentOwner
)psiElement
);
75 else if (psiElement
instanceof PsiDocComment
) {
76 processElementComment(psiElement
.getParent());
80 private void replaceDocComment(String newCommentText
, final PsiDocCommentOwner psiDocCommentOwner
) {
81 final PsiDocComment oldComment
= psiDocCommentOwner
.getDocComment();
82 if (newCommentText
!= null) newCommentText
= stripSpaces(newCommentText
);
83 if (newCommentText
== null || oldComment
== null || newCommentText
.equals(oldComment
.getText())) {
87 PsiComment newComment
= JavaPsiFacade
.getInstance(myProject
).getElementFactory().createCommentFromText(
88 newCommentText
, null);
89 final ASTNode oldNode
= oldComment
.getNode();
90 final ASTNode newNode
= newComment
.getNode();
91 assert oldNode
!= null && newNode
!= null;
92 final ASTNode parent
= oldNode
.getTreeParent();
93 parent
.replaceChild(oldNode
, newNode
); //important to replace with tree operation to avoid resolve and repository update
95 catch (IncorrectOperationException e
) {
100 private static String
stripSpaces(String text
) {
101 String
[] lines
= LineTokenizer
.tokenize(text
.toCharArray(), false);
102 StringBuffer buf
= new StringBuffer(text
.length());
103 for (int i
= 0; i
< lines
.length
; i
++) {
104 if (i
> 0) buf
.append('\n');
105 buf
.append(rTrim(lines
[i
]));
107 return buf
.toString();
110 private static String
rTrim(String text
) {
111 int idx
= text
.length();
113 if (!Character
.isWhitespace(text
.charAt(idx
-1))) break;
116 return text
.substring(0, idx
);
120 private String
formatClassComment(PsiClass psiClass
) {
121 final String info
= getOrigCommentInfo(psiClass
);
122 if (info
== null) return null;
124 JDComment comment
= getParser().parse(info
, new JDClassComment(this));
125 return comment
.generate(getIndent(psiClass
));
129 private String
formatMethodComment(PsiMethod psiMethod
) {
130 final String info
= getOrigCommentInfo(psiMethod
);
131 if (info
== null) return null;
133 JDComment comment
= getParser().parse(info
, new JDMethodComment(this));
134 return comment
.generate(getIndent(psiMethod
));
138 private String
formatFieldComment(PsiField psiField
) {
139 final String info
= getOrigCommentInfo(psiField
);
140 if (info
== null) return null;
142 JDComment comment
= getParser().parse(info
, new JDComment(this));
143 return comment
.generate(getIndent(psiField
));
147 * Returns the original comment info of the specified element or null
149 * @param element the specified element
152 private static String
getOrigCommentInfo(PsiDocCommentOwner element
) {
153 StringBuffer sb
= new StringBuffer();
154 PsiElement e
= element
.getFirstChild();
155 if (!(e
instanceof PsiComment
)) {
156 // no comments for this element
160 boolean first
= true;
162 if (e
instanceof PsiDocComment
) {
163 PsiComment cm
= (PsiComment
)e
;
164 String text
= cm
.getText();
165 if (text
.startsWith("//")) {
166 if (!first
) sb
.append('\n');
167 sb
.append(text
.substring(2).trim());
169 else if (text
.startsWith("/*")) {
170 if (text
.charAt(2) == '*') {
171 text
= text
.substring(3, Math
.max(3, text
.length() - 2));
174 text
= text
.substring(2, Math
.max(2, text
.length() - 2));
179 else if (!(e
instanceof PsiWhiteSpace
) && !(e
instanceof PsiComment
)) {
183 e
= e
.getNextSibling();
186 return sb
.toString();
191 * For the specified element returns its indentation
193 * @param element the specified element
194 * @return indentation as string
196 private static String
getIndent(PsiElement element
) {
197 PsiElement e
= element
.getFirstChild();
198 PsiWhiteSpace lastWS
= null;
199 for (; ; e
= e
.getNextSibling()) {
200 if (e
instanceof PsiWhiteSpace
) {
201 lastWS
= (PsiWhiteSpace
)e
;
203 else if (e
instanceof PsiComment
) {
211 e
= lastWS
== null ? element
.getPrevSibling() : lastWS
;
212 if (!(e
instanceof PsiWhiteSpace
)) return "";
213 PsiWhiteSpace ws
= (PsiWhiteSpace
)e
;
214 String t
= ws
.getText();
218 char ch
= t
.charAt(i
);
219 if (ch
== '\n' || ch
== '\r') break;
223 if (i
== l
) return "";
224 return t
.substring(i
);