change signature: cleanup
[idea/community.git] / platform / lang-impl / src / com / intellij / refactoring / changeSignature / ChangeSignatureGestureDetector.java
1 /*
2  * Copyright 2000-2010 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package com.intellij.refactoring.changeSignature;
17
18 import com.intellij.openapi.command.CommandProcessor;
19 import com.intellij.openapi.components.ProjectComponent;
20 import com.intellij.openapi.diagnostic.Logger;
21 import com.intellij.openapi.editor.Document;
22 import com.intellij.openapi.editor.EditorBundle;
23 import com.intellij.openapi.editor.EditorFactory;
24 import com.intellij.openapi.editor.actions.EditorActionUtil;
25 import com.intellij.openapi.editor.event.DocumentAdapter;
26 import com.intellij.openapi.editor.event.DocumentEvent;
27 import com.intellij.openapi.editor.event.EditorFactoryEvent;
28 import com.intellij.openapi.editor.event.EditorFactoryListener;
29 import com.intellij.openapi.project.Project;
30 import com.intellij.openapi.util.Comparing;
31 import com.intellij.psi.*;
32 import com.intellij.util.containers.HashMap;
33 import org.jetbrains.annotations.NotNull;
34
35 import java.util.Map;
36
37 /**
38  * User: anna
39  * Date: Sep 6, 2010
40  */
41 public class ChangeSignatureGestureDetector extends PsiTreeChangeAdapter implements ProjectComponent, EditorFactoryListener {
42   private final Project myProject;
43   private final Map<PsiFile, MyDocumentChangeAdapter> myListenerMap = new HashMap<PsiFile, MyDocumentChangeAdapter>();
44   private static final Logger LOG = Logger.getInstance("#" + ChangeSignatureGestureDetector.class.getName());
45   private boolean myDeaf = false;
46
47   public ChangeSignatureGestureDetector(Project project) {
48     myProject = project;
49   }
50
51   public static ChangeSignatureGestureDetector getInstance(Project project){
52     return project.getComponent(ChangeSignatureGestureDetector.class);
53   }
54
55   public boolean isChangeSignatureAvailable(@NotNull PsiElement element) {
56     final MyDocumentChangeAdapter adapter = myListenerMap.get(element.getContainingFile());
57     if (adapter != null && adapter.getCurrentInfo() != null) {
58       final LanguageChangeSignatureDetector detector = LanguageChangeSignatureDetectors.INSTANCE.forLanguage(element.getLanguage());
59       LOG.assertTrue(detector != null);
60       return detector.isChangeSignatureAvailable(element, adapter.getCurrentInfo());
61     }
62     return false;
63   }
64
65   public boolean containsChangeSignatureChange(@NotNull PsiFile file) {
66     final MyDocumentChangeAdapter adapter = myListenerMap.get(file);
67     return adapter != null && adapter.getCurrentInfo() != null;
68   }
69
70   public void changeSignature(PsiFile file) {
71     try {
72       myDeaf = true;
73       final MyDocumentChangeAdapter changeBean = myListenerMap.get(file);
74       final ChangeInfo currentInfo = changeBean.getCurrentInfo();
75       final LanguageChangeSignatureDetector detector = LanguageChangeSignatureDetectors.INSTANCE.forLanguage(currentInfo.getLanguage());
76       if (detector.showDialog(currentInfo, changeBean.getInitialText())) {
77         changeBean.reinit();
78       }
79     }
80     finally {
81       myDeaf = false;
82     }
83   }
84
85   @Override
86   public void projectOpened() {
87     PsiManager.getInstance(myProject).addPsiTreeChangeListener(this);
88     EditorFactory.getInstance().addEditorFactoryListener(this);
89   }
90
91   @Override
92   public void projectClosed() {
93     final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(myProject);
94     for (PsiFile file : myListenerMap.keySet()) {
95       final MyDocumentChangeAdapter adapter = myListenerMap.get(file);
96       if (adapter != null) {
97         final Document document = documentManager.getDocument(file);
98         if (document != null) {
99           document.removeDocumentListener(adapter);
100         }
101       }
102     }
103     myListenerMap.clear();
104
105     PsiManager.getInstance(myProject).removePsiTreeChangeListener(this);
106     EditorFactory.getInstance().removeEditorFactoryListener(this);
107   }
108
109   @NotNull
110   @Override
111   public String getComponentName() {
112     return "ChangeSignatureGestureDetector";
113   }
114
115   @Override
116   public void initComponent() {
117   }
118
119   @Override
120   public void disposeComponent() {
121   }
122
123   @Override
124   public void childRemoved(PsiTreeChangeEvent event) {
125     change(event.getParent());
126   }
127
128   @Override
129   public void childReplaced(PsiTreeChangeEvent event) {
130     change(event.getChild());
131   }
132
133   @Override
134   public void childAdded(PsiTreeChangeEvent event) {
135     change(event.getChild());
136   }
137
138   private void change(PsiElement child) {
139     if (myDeaf) return;
140     if (child == null) return;
141     final PsiFile file = child.getContainingFile();
142     if (file != null) {
143       final MyDocumentChangeAdapter changeBean = myListenerMap.get(file);
144       if (changeBean != null && changeBean.getInitialText() != null) {
145         final ChangeInfo info = LanguageChangeSignatureDetectors.createCurrentChangeInfo(child, changeBean.getInitialChangeInfo());
146         if (info == null) {
147           changeBean.reinit();
148         } else if (!info.equals(changeBean.getInitialChangeInfo())) {
149           changeBean.setCurrentInfo(info);
150         }
151       }
152     }
153   }
154
155   @Override
156   public void editorCreated(EditorFactoryEvent event) {
157     addDocListener(event.getEditor().getDocument());
158   }
159
160   public void addDocListener(Document document) {
161     final PsiFile file = PsiDocumentManager.getInstance(myProject).getPsiFile(document);
162     if (file != null) {
163       final MyDocumentChangeAdapter adapter = new MyDocumentChangeAdapter();
164       document.addDocumentListener(adapter);
165       myListenerMap.put(file, adapter);
166     }
167   }
168
169   @Override
170   public void editorReleased(EditorFactoryEvent event) {
171     removeDocListener(event.getEditor().getDocument());
172   }
173
174   public void removeDocListener(Document document) {
175     final PsiFile file = PsiDocumentManager.getInstance(myProject).getPsiFile(document);
176     final MyDocumentChangeAdapter adapter = myListenerMap.remove(file);
177     if (adapter != null) {
178       document.removeDocumentListener(adapter);
179     }
180   }
181
182   public void clearSignatureChange(PsiFile file) {
183     final MyDocumentChangeAdapter adapter = myListenerMap.get(file);
184     if (adapter != null) {
185       adapter.setBannedChangeInfo(adapter.getCurrentInfo());
186       adapter.reinit();
187     }
188   }
189
190   private class MyDocumentChangeAdapter extends DocumentAdapter {
191     private String myInitialText;
192     private ChangeInfo myInitialChangeInfo;
193     private ChangeInfo myCurrentInfo;
194     private ChangeInfo myBannedChangeInfo;
195
196     public void setCurrentInfo(ChangeInfo currentInfo) {
197       myCurrentInfo = currentInfo;
198     }
199
200     public String getInitialText() {
201       return myInitialText;
202     }
203
204     public ChangeInfo getCurrentInfo() {
205       return myCurrentInfo;
206     }
207
208     @Override
209     public void beforeDocumentChange(DocumentEvent e) {
210       if (myDeaf) return;
211       if (myInitialText == null) {
212         final Document document = e.getDocument();
213         final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(myProject);
214         if (!documentManager.isUncommited(document)) {
215           final CommandProcessor processor = CommandProcessor.getInstance();
216           final String currentCommandName = processor.getCurrentCommandName();
217           if (!Comparing.strEqual(EditorBundle.message("typing.in.editor.command.name"), currentCommandName) &&
218               !Comparing.strEqual(EditorBundle.message("paste.command.name"), currentCommandName) &&
219               !Comparing.strEqual(LanguageChangeSignatureDetector.MOVE_PARAMETER, currentCommandName) &&
220               !Comparing.equal(EditorActionUtil.DELETE_COMMAND_GROUP, processor.getCurrentCommandGroupId())) {
221             return;
222           }
223           final PsiFile file = documentManager.getPsiFile(document);
224           if (file != null) {
225             final PsiElement element = file.findElementAt(e.getOffset());
226             if (element != null) {
227               if (myBannedChangeInfo != null && LanguageChangeSignatureDetectors.wasBanned(element, myBannedChangeInfo)) return;
228               myBannedChangeInfo = null;
229               final ChangeInfo info = LanguageChangeSignatureDetectors.createCurrentChangeInfo(element, myCurrentInfo);
230               if (info != null) {
231                 myInitialText = document.getText();
232                 myInitialChangeInfo = info;
233               }
234             }
235           }
236         }
237       }
238     }
239
240     public ChangeInfo getInitialChangeInfo() {
241       return myInitialChangeInfo;
242     }
243
244     public void setBannedChangeInfo(ChangeInfo bannedChangeInfo) {
245       myBannedChangeInfo = bannedChangeInfo;
246     }
247
248     public void reinit() {
249       myInitialText = null;
250       myInitialChangeInfo = null;
251       myCurrentInfo = null;
252     }
253   }
254
255 }