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
.codeInsight
.javadoc
;
18 import com
.intellij
.openapi
.diagnostic
.Logger
;
19 import com
.intellij
.openapi
.project
.Project
;
20 import com
.intellij
.openapi
.roots
.JavadocOrderRootType
;
21 import com
.intellij
.openapi
.roots
.ex
.ProjectRootManagerEx
;
22 import com
.intellij
.openapi
.util
.text
.StringUtil
;
23 import com
.intellij
.openapi
.vfs
.VirtualFile
;
24 import com
.intellij
.openapi
.vfs
.ex
.http
.HttpFileSystem
;
25 import com
.intellij
.psi
.*;
26 import com
.intellij
.psi
.codeStyle
.CodeStyleSettings
;
27 import com
.intellij
.psi
.codeStyle
.CodeStyleSettingsManager
;
28 import com
.intellij
.psi
.util
.PsiTreeUtil
;
29 import com
.intellij
.psi
.util
.TypeConversionUtil
;
30 import com
.intellij
.util
.ArrayUtil
;
31 import com
.intellij
.util
.IncorrectOperationException
;
32 import org
.jetbrains
.annotations
.NonNls
;
33 import org
.jetbrains
.annotations
.Nullable
;
35 import java
.util
.ArrayList
;
36 import java
.util
.LinkedList
;
37 import java
.util
.StringTokenizer
;
38 import java
.util
.regex
.Matcher
;
39 import java
.util
.regex
.Pattern
;
40 import java
.util
.regex
.PatternSyntaxException
;
42 public class JavaDocUtil
{
43 private static final Logger LOG
= Logger
.getInstance("#com.intellij.codeInsight.javadoc.JavaDocUtil");
45 private static final @NonNls Pattern ourTypePattern
= Pattern
.compile("[ ]+[^ ^\\[^\\]]");
46 private static final @NonNls Pattern ourLtFixupPattern
= Pattern
.compile("<([^/^\\w^!])");
47 private static final @NonNls Pattern ourToQuote
= Pattern
.compile("[\\\\\\.\\^\\$\\?\\*\\+\\|\\)\\}\\]\\{\\(\\[]");
48 private static final @NonNls String LT_ENTITY
= "<";
50 private JavaDocUtil() {
53 public static String
[] getDocPaths(Project project
) {
54 ArrayList
<String
> result
= new ArrayList
<String
>();
56 final VirtualFile
[] roots
= ProjectRootManagerEx
.getInstanceEx(project
).getFilesFromAllModules(JavadocOrderRootType
.getInstance());
57 for (VirtualFile root
: roots
) {
58 if (!(root
.getFileSystem() instanceof HttpFileSystem
)) {
59 result
.add(root
.getUrl());
63 return ArrayUtil
.toStringArray(result
);
67 * Extracts a reference to a source element from the beginning of the text.
69 * @return length of the extracted reference
71 public static int extractReference(String text
) {
72 int lparenthIndex
= text
.indexOf('(');
73 int spaceIndex
= text
.indexOf(' ');
75 spaceIndex
= text
.length();
77 if (lparenthIndex
< 0) {
81 if (spaceIndex
< lparenthIndex
) {
84 int rparenthIndex
= text
.indexOf(')', lparenthIndex
);
85 if (rparenthIndex
< 0) {
86 rparenthIndex
= text
.length() - 1;
88 return rparenthIndex
+ 1;
93 public static PsiElement
findReferenceTarget(PsiManager manager
, String refText
, PsiElement context
) {
94 LOG
.assertTrue(context
== null || context
.isValid());
96 int poundIndex
= refText
.indexOf('#');
97 final JavaPsiFacade facade
= JavaPsiFacade
.getInstance(manager
.getProject());
99 PsiClass aClass
= facade
.getResolveHelper().resolveReferencedClass(refText
, context
);
101 if (aClass
== null) aClass
= facade
.findClass(refText
, context
.getResolveScope());
103 if (aClass
!= null) return aClass
.getNavigationElement();
104 PsiPackage aPackage
= facade
.findPackage(refText
);
105 if (aPackage
!=null) return aPackage
;
109 String classRef
= refText
.substring(0, poundIndex
).trim();
110 if (classRef
.length() > 0) {
111 PsiClass aClass
= facade
.getResolveHelper().resolveReferencedClass(classRef
, context
);
113 if (aClass
== null) aClass
= facade
.findClass(classRef
, context
.getResolveScope());
115 if (aClass
== null) return null;
116 return findReferencedMember(aClass
, refText
.substring(poundIndex
+ 1), context
);
119 String memberRefText
= refText
.substring(1);
120 PsiElement scope
= context
;
122 if (scope
instanceof PsiFile
) break;
123 if (scope
instanceof PsiClass
) {
124 PsiElement member
= findReferencedMember((PsiClass
)scope
, memberRefText
, context
);
125 if (member
!= null) return member
;
127 scope
= scope
.getParent();
135 private static PsiElement
findReferencedMember(PsiClass aClass
, String memberRefText
, PsiElement context
) {
136 int parenthIndex
= memberRefText
.indexOf('(');
137 if (parenthIndex
< 0) {
138 String name
= memberRefText
;
139 PsiField field
= aClass
.findFieldByName(name
, true);
140 if (field
!= null) return field
.getNavigationElement();
141 PsiClass inner
= aClass
.findInnerClassByName(name
, true);
142 if (inner
!= null) return inner
.getNavigationElement();
143 PsiMethod
[] methods
= aClass
.getAllMethods();
144 for (PsiMethod method
: methods
) {
145 if (method
.getName().equals(name
)) return method
.getNavigationElement();
150 String name
= memberRefText
.substring(0, parenthIndex
).trim();
151 int rparenIndex
= memberRefText
.lastIndexOf(')');
152 if (rparenIndex
== -1) return null;
154 String parmsText
= memberRefText
.substring(parenthIndex
+ 1, rparenIndex
).trim();
155 StringTokenizer tokenizer
= new StringTokenizer(parmsText
.replaceAll("[*]", ""), ",");
156 PsiType
[] types
= new PsiType
[tokenizer
.countTokens()];
158 PsiElementFactory factory
= JavaPsiFacade
.getInstance(aClass
.getProject()).getElementFactory();
159 while (tokenizer
.hasMoreTokens()) {
160 String parmText
= tokenizer
.nextToken().trim();
162 Matcher typeMatcher
= ourTypePattern
.matcher(parmText
);
163 String typeText
= parmText
;
165 if (typeMatcher
.find()) {
166 typeText
= parmText
.substring(0, typeMatcher
.start());
169 PsiType type
= factory
.createTypeFromText(typeText
, context
);
172 catch (IncorrectOperationException e
) {
176 PsiMethod
[] methods
= aClass
.findMethodsByName(name
, true);
178 for (PsiMethod method
: methods
) {
179 PsiParameter
[] parms
= method
.getParameterList().getParameters();
180 if (parms
.length
!= types
.length
) continue;
182 for (int k
= 0; k
< parms
.length
; k
++) {
183 PsiParameter parm
= parms
[k
];
186 !TypeConversionUtil
.erasure(parm
.getType()).getCanonicalText().equals(types
[k
].getCanonicalText()) &&
187 !parm
.getType().getCanonicalText().equals(types
[k
].getCanonicalText())
189 continue MethodsLoop
;
193 int hashIndex
= memberRefText
.indexOf('#',rparenIndex
);
194 if (hashIndex
!= -1) {
195 int parameterNumber
= Integer
.parseInt(memberRefText
.substring(hashIndex
+ 1));
196 if (parameterNumber
< parms
.length
) return method
.getParameterList().getParameters()[parameterNumber
].getNavigationElement();
198 return method
.getNavigationElement();
205 public static String
getReferenceText(Project project
, PsiElement element
) {
206 if (element
instanceof PsiPackage
) {
207 return ((PsiPackage
)element
).getQualifiedName();
209 else if (element
instanceof PsiClass
) {
210 final String refText
= ((PsiClass
)element
).getQualifiedName();
211 if (refText
!= null) return refText
;
212 return ((PsiClass
)element
).getName();
214 else if (element
instanceof PsiField
) {
215 PsiField field
= (PsiField
)element
;
216 String name
= field
.getName();
217 PsiClass aClass
= field
.getContainingClass();
218 if (aClass
!= null) {
219 return getReferenceText(project
, aClass
) + "#" + name
;
225 else if (element
instanceof PsiMethod
) {
226 PsiMethod method
= (PsiMethod
)element
;
227 String name
= method
.getName();
228 StringBuffer buffer
= new StringBuffer();
229 PsiClass aClass
= method
.getContainingClass();
230 if (aClass
!= null) {
231 buffer
.append(getReferenceText(project
, aClass
));
236 PsiParameter
[] parms
= method
.getParameterList().getParameters();
237 CodeStyleSettings styleSettings
= CodeStyleSettingsManager
.getSettings(project
);
238 boolean spaceBeforeComma
= styleSettings
.SPACE_BEFORE_COMMA
;
239 boolean spaceAfterComma
= styleSettings
.SPACE_AFTER_COMMA
;
240 for (int i
= 0; i
< parms
.length
; i
++) {
241 PsiParameter parm
= parms
[i
];
242 String typeText
= TypeConversionUtil
.erasure(parm
.getType()).getCanonicalText();
243 buffer
.append(typeText
);
244 if (i
< parms
.length
- 1) {
245 if (spaceBeforeComma
) {
249 if (spaceAfterComma
) {
255 return buffer
.toString();
257 else if (element
instanceof PsiParameter
) {
258 final PsiMethod method
= PsiTreeUtil
.getParentOfType(element
, PsiMethod
.class);
259 if (method
!= null) {
260 return getReferenceText(project
, method
) +
262 ((PsiParameterList
)element
.getParent()).getParameterIndex((PsiParameter
)element
);
269 public static String
getShortestClassName(PsiClass aClass
, PsiElement context
) {
270 @NonNls String shortName
= aClass
.getName();
271 if(shortName
== null){
274 PsiClass containingClass
= aClass
.getContainingClass();
275 while (containingClass
!= null) {
276 shortName
= containingClass
.getName() + "." + shortName
;
277 containingClass
= containingClass
.getContainingClass();
280 String qName
= aClass
.getQualifiedName();
281 if (qName
== null) return shortName
;
283 final PsiManager manager
= aClass
.getManager();
284 return manager
.areElementsEquivalent(aClass
, JavaPsiFacade
.getInstance(manager
.getProject()).getResolveHelper().resolveReferencedClass(shortName
, context
))
289 public static String
getLabelText(Project project
, PsiManager manager
, String refText
, PsiElement context
) {
290 PsiElement refElement
= findReferenceTarget(manager
, refText
, context
);
291 if (refElement
== null) {
292 return refText
.replaceFirst("^#", "").replaceAll("#", ".");
294 int poundIndex
= refText
.indexOf('#');
295 if (poundIndex
< 0) {
296 if (refElement
instanceof PsiClass
) {
297 return getShortestClassName((PsiClass
)refElement
, context
);
304 PsiClass aClass
= null;
305 if (refElement
instanceof PsiField
) {
306 aClass
= ((PsiField
)refElement
).getContainingClass();
308 else if (refElement
instanceof PsiMethod
) {
309 aClass
= ((PsiMethod
)refElement
).getContainingClass();
311 else if (refElement
instanceof PsiClass
){
312 return refText
.replaceAll("#", ".");
314 if (aClass
== null) return refText
;
315 String classRef
= refText
.substring(0, poundIndex
).trim();
316 String memberText
= refText
.substring(poundIndex
+ 1);
317 String memberLabel
= getMemberLabelText(project
, manager
, memberText
, context
);
318 if (classRef
.length() > 0) {
319 PsiElement refClass
= findReferenceTarget(manager
, classRef
, context
);
320 if (refClass
instanceof PsiClass
) {
321 PsiElement scope
= context
;
323 if (scope
== null || scope
instanceof PsiFile
) break;
324 if (scope
.equals(refClass
)) {
327 scope
= scope
.getParent();
330 return getLabelText(project
, manager
, classRef
, context
) + "." + memberLabel
;
338 private static String
getMemberLabelText(Project project
, PsiManager manager
, String memberText
, PsiElement context
) {
339 int parenthIndex
= memberText
.indexOf('(');
340 if (parenthIndex
< 0) return memberText
;
341 if (!StringUtil
.endsWithChar(memberText
, ')')) return memberText
;
342 String parms
= memberText
.substring(parenthIndex
+ 1, memberText
.length() - 1);
343 StringBuffer buffer
= new StringBuffer();
344 CodeStyleSettings styleSettings
= CodeStyleSettingsManager
.getSettings(project
);
345 boolean spaceBeforeComma
= styleSettings
.SPACE_BEFORE_COMMA
;
346 boolean spaceAfterComma
= styleSettings
.SPACE_AFTER_COMMA
;
347 StringTokenizer tokenizer
= new StringTokenizer(parms
, ",");
348 while (tokenizer
.hasMoreTokens()) {
349 String param
= tokenizer
.nextToken().trim();
350 int index1
= param
.indexOf('[');
351 if (index1
< 0) index1
= param
.length();
352 int index2
= param
.indexOf(' ');
353 if (index2
< 0) index2
= param
.length();
354 int index
= Math
.min(index1
, index2
);
355 String className
= param
.substring(0, index
).trim();
356 String shortClassName
= getLabelText(project
, manager
, className
, context
);
357 buffer
.append(shortClassName
);
358 buffer
.append(param
.substring(className
.length()));
359 if (tokenizer
.hasMoreElements()) {
360 if (spaceBeforeComma
) {
364 if (spaceAfterComma
) {
369 return memberText
.substring(0, parenthIndex
+ 1) + buffer
.toString() + ")";
372 private static String
quote(String x
) {
373 if (ourToQuote
.matcher(x
).find()) {
380 public static String
fixupText(String docText
) {
381 Matcher fixupMatcher
= ourLtFixupPattern
.matcher(docText
);
382 LinkedList
<String
> secondSymbols
= new LinkedList
<String
>();
384 while (fixupMatcher
.find()) {
385 String s
= fixupMatcher
.group(1);
387 //[db] that's workaround to avoid internal bug
388 if (!s
.equals("\\")) {
389 secondSymbols
.addFirst(s
);
393 for (String s
: secondSymbols
) {
394 String pattern
= "<" + quote(s
);
397 docText
= Pattern
.compile(pattern
).matcher(docText
).replaceAll(LT_ENTITY
+ pattern
);
399 catch (PatternSyntaxException e
) {
400 LOG
.error("Pattern syntax exception on " + pattern
);
407 public static PsiClassType
[] getImplementsList(PsiClass aClass
) {
408 if (aClass
instanceof PsiAnonymousClass
) {
409 return new PsiClassType
[]{((PsiAnonymousClass
)aClass
).getBaseClassType()};
412 PsiReferenceList list
= aClass
.getImplementsList();
414 return list
== null ? PsiClassType
.EMPTY_ARRAY
: list
.getReferencedTypes();
417 public static PsiClassType
[] getExtendsList(PsiClass aClass
) {
418 if (aClass
instanceof PsiAnonymousClass
) {
419 return new PsiClassType
[]{((PsiAnonymousClass
)aClass
).getBaseClassType()};
422 PsiReferenceList list
= aClass
.getExtendsList();
424 return list
== null ? PsiClassType
.EMPTY_ARRAY
: list
.getReferencedTypes();