update copyright
[fedora-idea.git] / java / java-impl / src / com / intellij / codeInsight / javadoc / JavaDocUtil.java
blobb7a798e0de418e9ed02b35031bcaceb336fdc504
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.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 = "&lt;";
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);
66 /**
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(' ');
74 if (spaceIndex < 0) {
75 spaceIndex = text.length();
77 if (lparenthIndex < 0) {
78 return spaceIndex;
80 else {
81 if (spaceIndex < lparenthIndex) {
82 return spaceIndex;
84 int rparenthIndex = text.indexOf(')', lparenthIndex);
85 if (rparenthIndex < 0) {
86 rparenthIndex = text.length() - 1;
88 return rparenthIndex + 1;
92 @Nullable
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());
98 if (poundIndex < 0) {
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;
106 return null;
108 else {
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);
118 else {
119 String memberRefText = refText.substring(1);
120 PsiElement scope = context;
121 while (true) {
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();
129 return null;
134 @Nullable
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();
147 return null;
149 else {
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()];
157 int i = 0;
158 PsiElementFactory factory = JavaPsiFacade.getInstance(aClass.getProject()).getElementFactory();
159 while (tokenizer.hasMoreTokens()) {
160 String parmText = tokenizer.nextToken().trim();
161 try {
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);
170 types[i++] = type;
172 catch (IncorrectOperationException e) {
173 LOG.info(e);
176 PsiMethod[] methods = aClass.findMethodsByName(name, true);
177 MethodsLoop:
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];
184 if (
185 types[k] != null &&
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();
200 return null;
204 @Nullable
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;
221 else {
222 return "#" + 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));
233 buffer.append("#");
234 buffer.append(name);
235 buffer.append("(");
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) {
246 buffer.append(" ");
248 buffer.append(",");
249 if (spaceAfterComma) {
250 buffer.append(" ");
254 buffer.append(")");
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) +
261 "#"+
262 ((PsiParameterList)element.getParent()).getParameterIndex((PsiParameter)element);
266 return null;
269 public static String getShortestClassName(PsiClass aClass, PsiElement context) {
270 @NonNls String shortName = aClass.getName();
271 if(shortName == null){
272 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))
285 ? shortName
286 : qName;
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);
299 else {
300 return refText;
303 else {
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;
322 while (true) {
323 if (scope == null || scope instanceof PsiFile) break;
324 if (scope.equals(refClass)) {
325 return memberLabel;
327 scope = scope.getParent();
330 return getLabelText(project, manager, classRef, context) + "." + memberLabel;
332 else {
333 return 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) {
361 buffer.append(" ");
363 buffer.append(",");
364 if (spaceAfterComma) {
365 buffer.append(" ");
369 return memberText.substring(0, parenthIndex + 1) + buffer.toString() + ")";
372 private static String quote(String x) {
373 if (ourToQuote.matcher(x).find()) {
374 return "\\" + x;
377 return x;
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);
396 try {
397 docText = Pattern.compile(pattern).matcher(docText).replaceAll(LT_ENTITY + pattern);
399 catch (PatternSyntaxException e) {
400 LOG.error("Pattern syntax exception on " + pattern);
404 return docText;
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();