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.
21 package com
.intellij
.codeInsight
;
23 import com
.intellij
.CommonBundle
;
24 import com
.intellij
.ProjectTopics
;
25 import com
.intellij
.codeInsight
.highlighting
.HighlightManager
;
26 import com
.intellij
.ide
.DataManager
;
27 import com
.intellij
.openapi
.application
.ApplicationManager
;
28 import com
.intellij
.openapi
.application
.Result
;
29 import com
.intellij
.openapi
.command
.WriteCommandAction
;
30 import com
.intellij
.openapi
.command
.undo
.UndoUtil
;
31 import com
.intellij
.openapi
.diagnostic
.Logger
;
32 import com
.intellij
.openapi
.editor
.Editor
;
33 import com
.intellij
.openapi
.editor
.LogicalPosition
;
34 import com
.intellij
.openapi
.editor
.ScrollType
;
35 import com
.intellij
.openapi
.editor
.colors
.EditorColors
;
36 import com
.intellij
.openapi
.editor
.colors
.EditorColorsManager
;
37 import com
.intellij
.openapi
.editor
.markup
.RangeHighlighter
;
38 import com
.intellij
.openapi
.editor
.markup
.TextAttributes
;
39 import com
.intellij
.openapi
.fileChooser
.FileChooser
;
40 import com
.intellij
.openapi
.fileChooser
.FileChooserDescriptor
;
41 import com
.intellij
.openapi
.fileChooser
.FileChooserDescriptorFactory
;
42 import com
.intellij
.openapi
.fileEditor
.FileEditorManager
;
43 import com
.intellij
.openapi
.project
.Project
;
44 import com
.intellij
.openapi
.project
.ProjectBundle
;
45 import com
.intellij
.openapi
.projectRoots
.SdkModificator
;
46 import com
.intellij
.openapi
.roots
.*;
47 import com
.intellij
.openapi
.roots
.libraries
.Library
;
48 import com
.intellij
.openapi
.ui
.Messages
;
49 import com
.intellij
.openapi
.ui
.popup
.JBPopupFactory
;
50 import com
.intellij
.openapi
.ui
.popup
.PopupStep
;
51 import com
.intellij
.openapi
.ui
.popup
.util
.BaseListPopupStep
;
52 import com
.intellij
.openapi
.util
.Comparing
;
53 import com
.intellij
.openapi
.util
.Disposer
;
54 import com
.intellij
.openapi
.util
.IconLoader
;
55 import com
.intellij
.openapi
.util
.TextRange
;
56 import com
.intellij
.openapi
.util
.text
.StringUtil
;
57 import com
.intellij
.openapi
.vfs
.LocalFileSystem
;
58 import com
.intellij
.openapi
.vfs
.ReadonlyStatusHandler
;
59 import com
.intellij
.openapi
.vfs
.VfsUtil
;
60 import com
.intellij
.openapi
.vfs
.VirtualFile
;
61 import com
.intellij
.psi
.*;
62 import com
.intellij
.psi
.codeStyle
.CodeStyleSettingsManager
;
63 import com
.intellij
.psi
.util
.PsiFormatUtil
;
64 import com
.intellij
.psi
.util
.PsiTreeUtil
;
65 import com
.intellij
.psi
.xml
.XmlDocument
;
66 import com
.intellij
.psi
.xml
.XmlFile
;
67 import com
.intellij
.psi
.xml
.XmlTag
;
68 import com
.intellij
.util
.ArrayUtil
;
69 import com
.intellij
.util
.IncorrectOperationException
;
70 import com
.intellij
.util
.StringBuilderSpinAllocator
;
71 import com
.intellij
.util
.containers
.ConcurrentWeakHashMap
;
72 import com
.intellij
.util
.messages
.MessageBusConnection
;
73 import com
.intellij
.util
.ui
.OptionsMessageDialog
;
74 import org
.jetbrains
.annotations
.NonNls
;
75 import org
.jetbrains
.annotations
.NotNull
;
76 import org
.jetbrains
.annotations
.Nullable
;
80 import java
.awt
.event
.ActionEvent
;
81 import java
.io
.IOException
;
83 import java
.util
.List
;
85 public class ExternalAnnotationsManagerImpl
extends ExternalAnnotationsManager
{
86 public static final Icon ICON
= IconLoader
.getIcon("/modules/annotation.png");
87 private static final Logger LOG
= Logger
.getInstance("#" + ExternalAnnotationsManagerImpl
.class.getName());
89 private final Map
<VirtualFile
, List
<XmlFile
>> myExternalAnotations
= new ConcurrentWeakHashMap
<VirtualFile
, List
<XmlFile
>>();
90 private static final List
<XmlFile
> NULL
= new ArrayList
<XmlFile
>();
91 private final PsiManager myPsiManager
;
93 public ExternalAnnotationsManagerImpl(final Project project
, final PsiManager psiManager
) {
94 myPsiManager
= psiManager
;
95 final MessageBusConnection connection
= project
.getMessageBus().connect(project
);
96 connection
.subscribe(ProjectTopics
.PROJECT_ROOTS
, new ModuleRootListener() {
97 public void beforeRootsChange(ModuleRootEvent event
) {
100 public void rootsChanged(ModuleRootEvent event
) {
101 myExternalAnotations
.clear();
107 public PsiAnnotation
findExternalAnnotation(final PsiModifierListOwner listOwner
, final String annotationFQN
) {
108 return collectExternalAnnotations(listOwner
).get(annotationFQN
);
112 public PsiAnnotation
[] findExternalAnnotations(final PsiModifierListOwner listOwner
) {
113 final Map
<String
, PsiAnnotation
> result
= collectExternalAnnotations(listOwner
);
114 return result
.isEmpty() ?
null : result
.values().toArray(new PsiAnnotation
[result
.size()]);
117 private Map
<String
, PsiAnnotation
> collectExternalAnnotations(final PsiModifierListOwner listOwner
) {
118 final Map
<String
, PsiAnnotation
> result
= new HashMap
<String
, PsiAnnotation
>();
119 final List
<XmlFile
> files
= findExternalAnnotationsFile(listOwner
);
121 for (XmlFile file
: files
) {
122 if (file
.isValid()) {
123 final XmlDocument document
= file
.getDocument();
124 if (document
!= null) {
125 final XmlTag rootTag
= document
.getRootTag();
126 if (rootTag
!= null) {
127 final String externalName
= PsiFormatUtil
.getExternalName(listOwner
, false);
128 final String oldExternalName
= getNormalizedExternalName(listOwner
);
129 for (final XmlTag tag
: rootTag
.getSubTags()) {
130 final String className
= tag
.getAttributeValue("name");
131 if (Comparing
.strEqual(className
, externalName
) || Comparing
.strEqual(className
, oldExternalName
)) {
132 for (XmlTag annotationTag
: tag
.getSubTags()) {
133 final String annotationFQN
= annotationTag
.getAttributeValue("name");
134 final StringBuilder buf
= new StringBuilder();
135 for (XmlTag annotationaParameter
: annotationTag
.getSubTags()) {
136 buf
.append(",").append(annotationaParameter
.getAttributeValue("name")).append("=")
137 .append(annotationaParameter
.getAttributeValue("value"));
139 final String annotationText
=
140 "@" + annotationFQN
+ (buf
.length() > 0 ?
"(" + StringUtil
.trimStart(buf
.toString(), ",") + ")" : "");
142 result
.put(annotationFQN
,
143 JavaPsiFacade
.getInstance(listOwner
.getProject()).getElementFactory().createAnnotationFromText(
144 annotationText
, null));
146 catch (IncorrectOperationException e
) {
161 public void annotateExternally(final PsiModifierListOwner listOwner
, final String annotationFQName
, final PsiFile fromFile
) {
162 final Project project
= listOwner
.getProject();
163 final PsiFile containingFile
= listOwner
.getContainingFile();
164 if (containingFile
instanceof PsiJavaFile
) {
165 final String packageName
= ((PsiJavaFile
)containingFile
).getPackageName();
166 final VirtualFile virtualFile
= containingFile
.getVirtualFile();
167 LOG
.assertTrue(virtualFile
!= null);
168 final List
<OrderEntry
> entries
= ProjectRootManager
.getInstance(project
).getFileIndex().getOrderEntriesForFile(virtualFile
);
169 if (!entries
.isEmpty()) {
170 for (final OrderEntry entry
: entries
) {
171 if (!(entry
instanceof ModuleOrderEntry
)) {
172 final VirtualFile
[] virtualFiles
= AnnotationOrderRootType
.getFiles(entry
);
173 if (virtualFiles
.length
> 0) {
174 chooseRootAndAnnotateExternally(listOwner
, annotationFQName
, fromFile
, project
, packageName
, virtualFile
, virtualFiles
);
177 if (ApplicationManager
.getApplication().isUnitTestMode() || ApplicationManager
.getApplication().isHeadlessEnvironment()) {
180 SwingUtilities
.invokeLater(new Runnable() {
182 setupRootAndAnnotateExternally(entry
, project
, listOwner
, annotationFQName
, fromFile
, packageName
, virtualFile
);
193 private void setupRootAndAnnotateExternally(final OrderEntry entry
, final Project project
, final PsiModifierListOwner listOwner
,
194 final String annotationFQName
,
195 final PsiFile fromFile
, final String packageName
, final VirtualFile virtualFile
) {
196 final FileChooserDescriptor descriptor
= FileChooserDescriptorFactory
.createSingleFolderDescriptor();
197 descriptor
.setTitle(ProjectBundle
.message("external.annotations.root.chooser.title", entry
.getPresentableName()));
198 descriptor
.setDescription(ProjectBundle
.message("external.annotations.root.chooser.description"));
199 final VirtualFile
[] files
= FileChooser
.chooseFiles(project
, descriptor
);
200 if (files
.length
> 0) {
201 new WriteCommandAction(project
) {
202 protected void run(final Result result
) throws Throwable
{
203 if (files
[0] != null) {
204 appendChosenAnnotationsRoot(entry
, files
[0]);
205 final List
<XmlFile
> xmlFiles
= findExternalAnnotationsFile(listOwner
);
206 if (xmlFiles
!= null) { //file already exists under appeared content root
207 if (ReadonlyStatusHandler
.getInstance(project
).ensureFilesWritable(xmlFiles
.get(0).getVirtualFile())
208 .hasReadonlyFiles()) {
211 annotateExternally(listOwner
, annotationFQName
, xmlFiles
.get(0), fromFile
);
214 final XmlFile annotationsXml
= createAnnotationsXml(files
[0], packageName
);
215 if (annotationsXml
!= null) {
216 final List
<XmlFile
> createdFiles
= new ArrayList
<XmlFile
>();
217 createdFiles
.add(annotationsXml
);
218 myExternalAnotations
.put(virtualFile
, createdFiles
);
220 annotateExternally(listOwner
, annotationFQName
, annotationsXml
, fromFile
);
228 private void chooseRootAndAnnotateExternally(final PsiModifierListOwner listOwner
, final String annotationFQName
, final PsiFile fromFile
,
229 final Project project
, final String packageName
, final VirtualFile virtualFile
,
230 final VirtualFile
[] virtualFiles
) {
231 if (virtualFiles
.length
> 1) {
232 JBPopupFactory
.getInstance().createListPopup(new BaseListPopupStep
<VirtualFile
>("Annotation Roots", virtualFiles
){
234 public PopupStep
onChosen(final VirtualFile file
, final boolean finalChoice
) {
235 annotateExternally(file
, listOwner
, project
, packageName
, virtualFile
, annotationFQName
, fromFile
);
241 public String
getTextFor(final VirtualFile value
) {
242 return value
.getPresentableUrl();
246 public Icon
getIconFor(final VirtualFile aValue
) {
249 }).showInBestPositionFor(DataManager
.getInstance().getDataContext());
251 annotateExternally(virtualFiles
[0], listOwner
, project
, packageName
, virtualFile
, annotationFQName
, fromFile
);
255 private void annotateExternally(final VirtualFile file
, final PsiModifierListOwner listOwner
, final Project project
,
256 final String packageName
,
257 final VirtualFile virtualFile
,
258 final String annotationFQName
,
259 final PsiFile fromFile
) {
260 final XmlFile
[] annotationsXml
= new XmlFile
[1];
261 List
<XmlFile
> xmlFiles
= findExternalAnnotationsFile(listOwner
);
262 if (xmlFiles
!= null) {
263 for (XmlFile xmlFile
: xmlFiles
) {
264 final VirtualFile vXmlFile
= xmlFile
.getVirtualFile();
265 assert vXmlFile
!= null;
266 if (VfsUtil
.isAncestor(file
, vXmlFile
, false)) {
267 annotationsXml
[0] = xmlFile
;
268 if (ReadonlyStatusHandler
.getInstance(project
).ensureFilesWritable(xmlFile
.getVirtualFile()).hasReadonlyFiles()) return;
272 xmlFiles
= new ArrayList
<XmlFile
>();
275 final List
<XmlFile
> annotationFiles
= new ArrayList
<XmlFile
>(xmlFiles
);
276 new WriteCommandAction(project
) {
277 protected void run(final Result result
) throws Throwable
{
278 if (annotationsXml
[0] == null) {
279 annotationsXml
[0] = createAnnotationsXml(file
, packageName
);
281 if (annotationsXml
[0] != null) {
282 annotationFiles
.add(annotationsXml
[0]);
283 myExternalAnotations
.put(virtualFile
, annotationFiles
);
284 annotateExternally(listOwner
, annotationFQName
, annotationsXml
[0], fromFile
);
290 public boolean deannotate(final PsiModifierListOwner listOwner
, final String annotationFQN
) {
291 final List
<XmlFile
> files
= findExternalAnnotationsFile(listOwner
);
293 for (XmlFile file
: files
) {
294 if (file
.isValid()) {
295 final XmlDocument document
= file
.getDocument();
296 if (document
!= null) {
297 final XmlTag rootTag
= document
.getRootTag();
298 if (rootTag
!= null) {
299 final String externalName
= PsiFormatUtil
.getExternalName(listOwner
, false);
300 final String oldExternalName
= getNormalizedExternalName(listOwner
);
301 for (final XmlTag tag
: rootTag
.getSubTags()) {
302 final String className
= tag
.getAttributeValue("name");
303 if (Comparing
.strEqual(className
, externalName
) || Comparing
.strEqual(className
, oldExternalName
)) {
304 for (XmlTag annotationTag
: tag
.getSubTags()) {
305 if (Comparing
.strEqual(annotationTag
.getAttributeValue("name"), annotationFQN
)) {
306 if (ReadonlyStatusHandler
.getInstance(file
.getProject())
307 .ensureFilesWritable(file
.getVirtualFile()).hasReadonlyFiles()) {
311 annotationTag
.delete();
312 if (tag
.getSubTags().length
== 0) {
316 catch (IncorrectOperationException e
) {
333 public AnnotationPlace
chooseAnnotationsPlace(@NotNull final PsiElement element
) {
334 if (!element
.isPhysical()) return AnnotationPlace
.IN_CODE
; //element just created
335 if (!element
.getManager().isInProject(element
)) return AnnotationPlace
.EXTERNAL
;
336 final Project project
= element
.getProject();
337 final PsiFile containingFile
= element
.getContainingFile();
338 final VirtualFile virtualFile
= containingFile
.getVirtualFile();
339 LOG
.assertTrue(virtualFile
!= null);
340 final List
<OrderEntry
> entries
= ProjectRootManager
.getInstance(project
).getFileIndex().getOrderEntriesForFile(virtualFile
);
341 if (!entries
.isEmpty()) {
342 for (OrderEntry entry
: entries
) {
343 if (!(entry
instanceof ModuleOrderEntry
)) {
344 if (AnnotationOrderRootType
.getUrls(entry
).length
> 0) {
345 return AnnotationPlace
.EXTERNAL
;
351 final MyExternalPromptDialog dialog
= new MyExternalPromptDialog(project
);
352 if (dialog
.isToBeShown()) {
353 final PsiElement highlightElement
= element
instanceof PsiNameIdentifierOwner
354 ?
((PsiNameIdentifierOwner
)element
).getNameIdentifier()
355 : element
.getNavigationElement();
356 LOG
.assertTrue(highlightElement
!= null);
357 final Editor editor
= FileEditorManager
.getInstance(project
).getSelectedTextEditor();
358 final List
<RangeHighlighter
> highlighters
= new ArrayList
<RangeHighlighter
>();
359 final boolean highlight
=
360 editor
!= null && editor
.getDocument() == PsiDocumentManager
.getInstance(project
).getDocument(containingFile
);
362 if (highlight
) { //do not highlight for batch inspections
363 final EditorColorsManager colorsManager
= EditorColorsManager
.getInstance();
364 final TextAttributes attributes
= colorsManager
.getGlobalScheme().getAttributes(EditorColors
.SEARCH_RESULT_ATTRIBUTES
);
365 final TextRange textRange
= highlightElement
.getTextRange();
366 HighlightManager
.getInstance(project
).addRangeHighlight(editor
,
367 textRange
.getStartOffset(), textRange
.getEndOffset(),
368 attributes
, true, highlighters
);
369 final LogicalPosition logicalPosition
= editor
.offsetToLogicalPosition(textRange
.getStartOffset());
370 editor
.getScrollingModel().scrollTo(logicalPosition
, ScrollType
.CENTER
);
374 if (dialog
.getExitCode() == 2) {
375 return AnnotationPlace
.EXTERNAL
;
377 else if (dialog
.getExitCode() == 1) {
378 return AnnotationPlace
.NOWHERE
;
384 HighlightManager
.getInstance(project
).removeSegmentHighlighter(editor
, highlighters
.get(0));
388 return AnnotationPlace
.IN_CODE
;
391 private void appendChosenAnnotationsRoot(final OrderEntry entry
, final VirtualFile vFile
) {
392 if (entry
instanceof LibraryOrderEntry
) {
393 Library library
= ((LibraryOrderEntry
)entry
).getLibrary();
394 LOG
.assertTrue(library
!= null);
395 final ModifiableRootModel rootModel
= ModuleRootManager
.getInstance(entry
.getOwnerModule()).getModifiableModel();
396 final Library
.ModifiableModel model
= library
.getModifiableModel();
397 model
.addRoot(vFile
, AnnotationOrderRootType
.getInstance());
401 else if (entry
instanceof ModuleSourceOrderEntry
) {
402 final ModifiableRootModel model
= ModuleRootManager
.getInstance(entry
.getOwnerModule()).getModifiableModel();
403 model
.setRootUrls(AnnotationOrderRootType
.getInstance(), ArrayUtil
.mergeArrays(
404 model
.getRootUrls(AnnotationOrderRootType
.getInstance()), new String
[]{vFile
.getUrl()}, String
.class));
407 else if (entry
instanceof JdkOrderEntry
) {
408 final SdkModificator sdkModificator
= ((JdkOrderEntry
)entry
).getJdk().getSdkModificator();
409 sdkModificator
.addRoot(vFile
, AnnotationOrderRootType
.getInstance());
410 sdkModificator
.commitChanges();
412 myExternalAnotations
.clear();
415 private static void annotateExternally(final PsiModifierListOwner listOwner
,
416 final String annotationFQName
,
417 @Nullable final XmlFile xmlFile
,
418 final PsiFile codeUsageFile
) {
419 if (xmlFile
== null) return;
421 final XmlDocument document
= xmlFile
.getDocument();
422 if (document
!= null) {
423 final XmlTag rootTag
= document
.getRootTag();
424 final String externalName
= PsiFormatUtil
.getExternalName(listOwner
, false);
425 if (rootTag
!= null) {
426 for (XmlTag tag
: rootTag
.getSubTags()) {
427 if (Comparing
.strEqual(tag
.getAttributeValue("name"), externalName
)) {
428 tag
.add(XmlElementFactory
.getInstance(xmlFile
.getProject()).createTagFromText("<annotation name=\'" + annotationFQName
+ "\'/>"));
432 @NonNls final String text
=
433 "<item name=\'" + externalName
+ "\'>\n" + " <annotation name=\'" + annotationFQName
+ "\'/>\n" + "</item>";
434 rootTag
.add(XmlElementFactory
.getInstance(xmlFile
.getProject()).createTagFromText(text
));
438 catch (IncorrectOperationException e
) {
442 if (codeUsageFile
.getVirtualFile().isInLocalFileSystem()) {
443 UndoUtil
.markPsiFileForUndo(codeUsageFile
);
449 private XmlFile
createAnnotationsXml(VirtualFile root
, String packageName
) {
450 final String
[] dirs
= packageName
.split("[\\.]");
451 for (String dir
: dirs
) {
452 if (dir
.length() == 0) break;
453 VirtualFile subdir
= root
.findChild(dir
);
454 if (subdir
== null) {
456 subdir
= root
.createChildDirectory(null, dir
);
458 catch (IOException e
) {
464 final PsiDirectory directory
= myPsiManager
.findDirectory(root
);
465 if (directory
== null) return null;
467 final PsiFile psiFile
= directory
.findFile(ANNOTATIONS_XML
);
468 if (psiFile
instanceof XmlFile
) {
469 return (XmlFile
)psiFile
;
473 return (XmlFile
)directory
474 .add(PsiFileFactory
.getInstance(myPsiManager
.getProject()).createFileFromText(ANNOTATIONS_XML
, "<root></root>"));
476 catch (IncorrectOperationException e
) {
483 private List
<XmlFile
> findExternalAnnotationsFile(PsiModifierListOwner listOwner
) {
484 final Project project
= listOwner
.getProject();
485 final PsiFile containingFile
= listOwner
.getContainingFile();
486 if (containingFile
instanceof PsiJavaFile
) {
487 final VirtualFile virtualFile
= containingFile
.getVirtualFile();
488 final List
<XmlFile
> files
= myExternalAnotations
.get(virtualFile
);
489 if (files
== NULL
) return null;
491 for (Iterator
<XmlFile
> it
= files
.iterator(); it
.hasNext();) {
492 if (!it
.next().isValid()) it
.remove();
496 final PsiJavaFile javaFile
= (PsiJavaFile
)containingFile
;
497 final String packageName
= javaFile
.getPackageName();
498 if (virtualFile
!= null) {
499 final List
<OrderEntry
> entries
= ProjectRootManager
.getInstance(project
).getFileIndex().getOrderEntriesForFile(virtualFile
);
500 for (OrderEntry entry
: entries
) {
501 if (!(entry
instanceof ModuleOrderEntry
)) {
502 List
<XmlFile
> possibleAnnotationsXmls
= null;
503 final String
[] externalUrls
= AnnotationOrderRootType
.getUrls(entry
);
504 for (String url
: externalUrls
) {
505 final VirtualFile ext
= LocalFileSystem
.getInstance()
506 .findFileByPath(VfsUtil
.urlToPath(url
) + "/" + packageName
.replace(".", "/") + "/" + ANNOTATIONS_XML
);
508 final PsiFile psiFile
= myPsiManager
.findFile(ext
);
509 if (psiFile
instanceof XmlFile
) {
510 if (possibleAnnotationsXmls
== null) {
511 possibleAnnotationsXmls
= new ArrayList
<XmlFile
>();
513 possibleAnnotationsXmls
.add((XmlFile
)psiFile
);
517 if (possibleAnnotationsXmls
!= null) {
518 myExternalAnotations
.put(virtualFile
, possibleAnnotationsXmls
);
519 return possibleAnnotationsXmls
;
524 myExternalAnotations
.put(virtualFile
, NULL
);
527 /*final VirtualFile virtualFile = containingFile.getVirtualFile(); //for java files only
528 if (virtualFile != null) {
529 final VirtualFile parent = virtualFile.getParent();
530 if (parent != null) {
531 final VirtualFile extFile = parent.findChild(ANNOTATIONS_XML);
532 if (extFile != null) {
533 return (XmlFile)psiManager.findFile(extFile);
542 private static String
getNormalizedExternalName(PsiModifierListOwner owner
) {
543 String externalName
= PsiFormatUtil
.getExternalName(owner
);
544 if (externalName
!= null) {
545 if (owner
instanceof PsiParameter
&& owner
.getParent() instanceof PsiParameterList
) {
546 final PsiMethod method
= PsiTreeUtil
.getParentOfType(owner
, PsiMethod
.class);
547 if (method
!= null) {
549 externalName
.substring(0, externalName
.lastIndexOf(' ') + 1) + method
.getParameterList().getParameterIndex((PsiParameter
)owner
);
552 final int idx
= externalName
.indexOf('(');
553 if (idx
== -1) return externalName
;
554 final StringBuilder buf
= StringBuilderSpinAllocator
.alloc();
556 final int rightIdx
= externalName
.indexOf(')');
557 final String
[] params
= externalName
.substring(idx
+ 1, rightIdx
).split(",");
558 buf
.append(externalName
.substring(0, idx
+ 1));
559 for (String param
: params
) {
560 param
= param
.trim();
561 final int spaceIdx
= param
.indexOf(' ');
562 buf
.append(spaceIdx
> -1 ? param
.substring(0, spaceIdx
) : param
).append(", ");
564 return StringUtil
.trimEnd(buf
.toString(), ", ") + externalName
.substring(rightIdx
);
567 StringBuilderSpinAllocator
.dispose(buf
);
573 private static class MyExternalPromptDialog
extends OptionsMessageDialog
{
574 private final Project myProject
;
575 private static final String ADD_IN_CODE
= ProjectBundle
.message("external.annotations.in.code.option");
576 private static final String MESSAGE
= ProjectBundle
.message("external.annotations.suggestion.message");
578 public MyExternalPromptDialog(final Project project
) {
579 super(project
, MESSAGE
, ProjectBundle
.message("external.annotation.prompt"), Messages
.getQuestionIcon());
582 Disposer
.register(myProject
, myDisposable
);
585 protected String
getOkActionName() {
589 protected String
getCancelActionName() {
590 return CommonBundle
.getCancelButtonText();
593 @SuppressWarnings({"NonStaticInitializer"})
594 protected Action
[] createActions() {
595 final Action okAction
= getOKAction();
596 assignMnemonic(ADD_IN_CODE
, okAction
);
597 final String externalName
= ProjectBundle
.message("external.annotations.external.option");
598 return new Action
[]{okAction
, new AbstractAction(externalName
) {
600 assignMnemonic(externalName
, this);
603 public void actionPerformed(final ActionEvent e
) {
605 setToBeShown(toBeShown(), true);
609 }, getCancelAction()};
612 protected boolean isToBeShown() {
613 if (ApplicationManager
.getApplication().isHeadlessEnvironment() || ApplicationManager
.getApplication().isUnitTestMode()) return false;
614 return CodeStyleSettingsManager
.getSettings(myProject
).USE_EXTERNAL_ANNOTATIONS
;
617 protected void setToBeShown(boolean value
, boolean onOk
) {
618 CodeStyleSettingsManager
.getSettings(myProject
).USE_EXTERNAL_ANNOTATIONS
= value
;
621 protected JComponent
createNorthPanel() {
622 final JPanel northPanel
= (JPanel
)super.createNorthPanel();
623 northPanel
.add(new JLabel(MESSAGE
), BorderLayout
.CENTER
);
627 protected boolean shouldSaveOptionsOnCancel() {