5 package com
.intellij
.internal
;
7 import com
.intellij
.codeInsight
.generation
.GenerateMembersUtil
;
8 import com
.intellij
.codeInsight
.generation
.GenerationInfo
;
9 import com
.intellij
.codeInsight
.generation
.PsiGenerationInfo
;
10 import com
.intellij
.ide
.IdeView
;
11 import com
.intellij
.ide
.util
.PackageChooserDialog
;
12 import com
.intellij
.ide
.util
.PackageUtil
;
13 import com
.intellij
.openapi
.actionSystem
.AnAction
;
14 import com
.intellij
.openapi
.actionSystem
.AnActionEvent
;
15 import com
.intellij
.openapi
.actionSystem
.DataKeys
;
16 import com
.intellij
.openapi
.actionSystem
.PlatformDataKeys
;
17 import com
.intellij
.openapi
.application
.Result
;
18 import com
.intellij
.openapi
.command
.CommandProcessor
;
19 import com
.intellij
.openapi
.command
.WriteCommandAction
;
20 import com
.intellij
.openapi
.editor
.Document
;
21 import com
.intellij
.openapi
.fileTypes
.StdFileTypes
;
22 import com
.intellij
.openapi
.project
.Project
;
23 import com
.intellij
.openapi
.ui
.DialogWrapper
;
24 import com
.intellij
.openapi
.util
.Condition
;
25 import com
.intellij
.openapi
.util
.Ref
;
26 import com
.intellij
.psi
.*;
27 import com
.intellij
.psi
.search
.GlobalSearchScope
;
28 import com
.intellij
.psi
.search
.PackageScope
;
29 import com
.intellij
.psi
.search
.searches
.ClassInheritorsSearch
;
30 import com
.intellij
.ui
.DocumentAdapter
;
31 import com
.intellij
.ui
.EditorTextField
;
32 import com
.intellij
.util
.IncorrectOperationException
;
33 import com
.intellij
.util
.containers
.ContainerUtil
;
34 import gnu
.trove
.THashMap
;
35 import gnu
.trove
.THashSet
;
38 import javax
.swing
.event
.DocumentEvent
;
41 import java
.util
.List
;
43 public class GenerateVisitorByHierarchyAction
extends AnAction
{
44 public GenerateVisitorByHierarchyAction() {
45 super("Generate Hierarchy Visitor");
48 public void actionPerformed(AnActionEvent e
) {
49 final Ref
<String
> visitorNameRef
= Ref
.create("MyVisitor");
50 final Ref
<PsiClass
> parentClassRef
= Ref
.create(null);
51 final Project project
= e
.getData(PlatformDataKeys
.PROJECT
);
52 assert project
!= null;
53 final JavaPsiFacade psiFacade
= JavaPsiFacade
.getInstance(project
);
54 final PsiNameHelper helper
= psiFacade
.getNameHelper();
55 final PackageChooserDialog dialog
= new PackageChooserDialog("Choose Visitor Name, Package and Parent Class", project
) {
59 protected JComponent
createCenterPanel() {
60 final JPanel panel
= new JPanel(new BorderLayout());
61 panel
.add(super.createCenterPanel(), BorderLayout
.CENTER
);
62 panel
.add(createNamePanel(), BorderLayout
.NORTH
);
63 panel
.add(createBaseClassPanel(), BorderLayout
.SOUTH
);
67 private JComponent
createNamePanel() {
68 final JPanel panel
= new JPanel(new BorderLayout());
69 panel
.add(new JLabel("Visitor Name"), BorderLayout
.WEST
);
70 final JTextField nameField
= new JTextField(visitorNameRef
.get());
71 nameField
.getDocument().addDocumentListener(new DocumentAdapter() {
72 protected void textChanged(final DocumentEvent e
) {
73 visitorNameRef
.set(nameField
.getText());
77 panel
.add(nameField
, BorderLayout
.CENTER
);
81 private JComponent
createBaseClassPanel() {
82 final JPanel panel
= new JPanel(new BorderLayout());
83 panel
.add(new JLabel("Hierarchy Base Class"), BorderLayout
.WEST
);
84 final PsiElementFactory factory
= psiFacade
.getElementFactory();
85 final PsiTypeCodeFragment codeFragment
= factory
.createTypeCodeFragment("", null, true, true);
86 final Document document
= PsiDocumentManager
.getInstance(project
).getDocument(codeFragment
);
87 final EditorTextField editorTextField
= new EditorTextField(document
, project
, StdFileTypes
.JAVA
);
88 editorTextField
.addDocumentListener(new com
.intellij
.openapi
.editor
.event
.DocumentAdapter() {
89 public void documentChanged(final com
.intellij
.openapi
.editor
.event
.DocumentEvent e
) {
90 parentClassRef
.set(null);
92 final PsiType psiType
= codeFragment
.getType();
93 final PsiClass psiClass
= psiType
instanceof PsiClassType ?
((PsiClassType
)psiType
).resolve() : null;
94 parentClassRef
.set(psiClass
);
96 catch (PsiTypeCodeFragment
.IncorrectTypeException e1
) {
102 panel
.add(editorTextField
.getComponent(), BorderLayout
.CENTER
);
106 private void updateOKAction() {
107 PsiDocumentManager
.getInstance(project
).commitAllDocuments();
108 final String message
;
109 if (!helper
.isQualifiedName(visitorNameRef
.get())) {
110 message
= "Visitor name is not valid";
112 else if (parentClassRef
.isNull()) {
113 message
= "Hierarchy parent should be specified";
115 else if (parentClassRef
.get().isAnnotationType() || parentClassRef
.get().isEnum()) {
116 message
= "Hierarchy parent should be interface or class";
119 setErrorText(message
);
120 setOKActionEnabled(message
== null);
124 final PsiElement element
= DataKeys
.PSI_ELEMENT
.getData(e
.getDataContext());
125 if (element
instanceof PsiPackage
) {
126 dialog
.selectPackage(((PsiPackage
)element
).getQualifiedName());
128 else if (element
instanceof PsiDirectory
) {
129 final PsiPackage aPackage
= JavaDirectoryService
.getInstance().getPackage((PsiDirectory
)element
);
130 if (aPackage
!= null) {
131 dialog
.selectPackage(aPackage
.getQualifiedName());
135 if (dialog
.getExitCode() != DialogWrapper
.OK_EXIT_CODE
||
136 dialog
.getSelectedPackage() == null ||
137 dialog
.getSelectedPackage().getQualifiedName().length() == 0 ||
138 parentClassRef
.isNull()) return;
139 final PsiPackage aPackage
= dialog
.getSelectedPackage();
140 final PsiClass psiClass
= parentClassRef
.get();
141 final String visitorName
= visitorNameRef
.get();
142 final String visitorQName
= PsiNameHelper
.getShortClassName(visitorName
).equals(visitorName
)? aPackage
.getQualifiedName()+"."+visitorName
: visitorName
;
143 generateVisitorClass(visitorQName
, aPackage
, psiClass
);
144 final IdeView ideView
= DataKeys
.IDE_VIEW
.getData(e
.getDataContext());
145 final PsiClass visitorClass
= JavaPsiFacade
.getInstance(project
).findClass(visitorQName
, GlobalSearchScope
.projectScope(project
));
146 if (ideView
!= null && visitorClass
!= null) {
147 ideView
.selectElement(visitorClass
);
151 public void update(final AnActionEvent e
) {
152 e
.getPresentation().setEnabled(e
.getData(PlatformDataKeys
.PROJECT
) != null);
155 private static void generateVisitorClass(final String visitorName
, final PsiPackage aPackage
, final PsiClass baseClass
) {
156 final THashMap
<PsiClass
, Set
<PsiClass
>> classes
= new THashMap
<PsiClass
, Set
<PsiClass
>>();
157 for (PsiClass aClass
: ClassInheritorsSearch
.search(baseClass
, new PackageScope(aPackage
, false, false), true).findAll()) {
158 if (aClass
.hasModifierProperty(PsiModifier
.ABSTRACT
) == baseClass
.hasModifierProperty(PsiModifier
.ABSTRACT
)) {
159 final List
<PsiClass
> implementors
=
160 ContainerUtil
.findAll(ClassInheritorsSearch
.search(aClass
).findAll(), new Condition
<PsiClass
>() {
161 public boolean value(final PsiClass psiClass
) {
162 return !psiClass
.hasModifierProperty(PsiModifier
.ABSTRACT
);
165 classes
.put(aClass
, new THashSet
<PsiClass
>(implementors
));
168 final THashMap
<PsiClass
, Set
<PsiClass
>> pathMap
= new THashMap
<PsiClass
, Set
<PsiClass
>>();
169 for (PsiClass aClass
: classes
.keySet()) {
170 final Set
<PsiClass
> superClasses
= new LinkedHashSet
<PsiClass
>();
171 for (PsiClass superClass
: aClass
.getSupers()) {
172 if (superClass
.isInheritor(baseClass
, true)) {
173 superClasses
.add(superClass
);
174 final Set
<PsiClass
> superImplementors
= classes
.get(superClass
);
175 if (superImplementors
!= null) {
176 superImplementors
.removeAll(classes
.get(aClass
));
180 if (superClasses
.isEmpty()) {
181 superClasses
.add(baseClass
);
183 pathMap
.put(aClass
, superClasses
);
185 pathMap
.put(baseClass
, Collections
.<PsiClass
>emptySet());
186 final ArrayList
<PsiFile
> psiFiles
= new ArrayList
<PsiFile
>();
187 for (Set
<PsiClass
> implementors
: classes
.values()) {
188 for (PsiClass psiClass
: implementors
) {
189 psiFiles
.add(psiClass
.getContainingFile());
192 final Project project
= baseClass
.getProject();
193 final PsiClass visitorClass
= JavaPsiFacade
.getInstance(project
).findClass(visitorName
, GlobalSearchScope
.projectScope(project
));
194 if (visitorClass
!= null) {
195 psiFiles
.add(visitorClass
.getContainingFile());
197 new WriteCommandAction(project
, psiFiles
.toArray(new PsiFile
[psiFiles
.size()])) {
199 protected void run(final Result result
) throws Throwable
{
200 CommandProcessor
.getInstance().markCurrentCommandAsComplex(project
);
201 if (visitorClass
== null) {
202 final String shortClassName
= PsiNameHelper
.getShortClassName(visitorName
);
203 final String packageName
= visitorName
.substring(0, visitorName
.length() - shortClassName
.length() - 1);
204 final PsiDirectory directory
= PackageUtil
.findOrCreateDirectoryForPackage(project
, packageName
, null, false);
205 if (directory
!= null) {
206 final PsiClass visitorClass
= JavaDirectoryService
.getInstance().createClass(directory
, shortClassName
);
207 generateVisitorClass(visitorClass
, classes
, pathMap
);
211 generateVisitorClass(visitorClass
, classes
, pathMap
);
217 private static void generateVisitorClass(final PsiClass visitorClass
, final Map
<PsiClass
, Set
<PsiClass
>> classes
,
218 final THashMap
<PsiClass
, Set
<PsiClass
>> pathMap
) throws Throwable
{
219 final PsiElementFactory elementFactory
= JavaPsiFacade
.getInstance(visitorClass
.getProject()).getElementFactory();
220 for (PsiClass psiClass
: classes
.keySet()) {
221 final PsiMethod method
= elementFactory
.createMethodFromText(
222 "public void accept(final " + visitorClass
.getQualifiedName() + " visitor) { visitor.visit" + psiClass
.getName() + "(this); }", psiClass
);
223 for (PsiClass implementor
: classes
.get(psiClass
)) {
224 addOrReplaceMethod(method
, implementor
);
228 final THashSet
<PsiClass
> visitedClasses
= new THashSet
<PsiClass
>();
229 final LinkedList
<PsiClass
> toProcess
= new LinkedList
<PsiClass
>(classes
.keySet());
230 while (!toProcess
.isEmpty()) {
231 final PsiClass psiClass
= toProcess
.removeFirst();
232 if (!visitedClasses
.add(psiClass
)) continue;
233 final Set
<PsiClass
> pathClasses
= pathMap
.get(psiClass
);
234 toProcess
.addAll(pathClasses
);
235 final StringBuilder methodText
= new StringBuilder();
237 methodText
.append("public void visit").append(psiClass
.getName()).append("(final ").append(psiClass
.getQualifiedName()).append(" o) {");
238 boolean first
= true;
239 for (PsiClass pathClass
: pathClasses
) {
240 if (first
) first
= false;
241 else methodText
.append("// ");
242 methodText
.append("visit").append(pathClass
.getName()).append("(o);\n");
244 methodText
.append("}");
245 final PsiMethod method
= elementFactory
.createMethodFromText(methodText
.toString(), psiClass
);
246 addOrReplaceMethod(method
, visitorClass
);
251 private static void addOrReplaceMethod(final PsiMethod method
, final PsiClass implementor
) throws IncorrectOperationException
{
252 final PsiMethod accept
= implementor
.findMethodBySignature(method
, false);
253 if (accept
!= null) {
254 accept
.replace(method
);
257 GenerateMembersUtil
.insertMembersAtOffset(implementor
.getContainingFile(), implementor
.getLastChild().getTextOffset(), Collections
.<GenerationInfo
>singletonList(new PsiGenerationInfo
<PsiMethod
>(method
)));