get rid of intellij.build.toolbox.litegen parameter and use BuildOptions.TOOLBOX_LITE...
[idea/community.git] / java / java-impl / src / com / intellij / codeInsight / generation / GenerateMembersHandlerBase.java
1 /*
2  * Copyright 2000-2016 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.codeInsight.generation;
17
18 import com.intellij.codeInsight.CodeInsightActionHandler;
19 import com.intellij.codeInsight.hint.HintManager;
20 import com.intellij.codeInsight.template.Template;
21 import com.intellij.codeInsight.template.TemplateEditingAdapter;
22 import com.intellij.codeInsight.template.TemplateManager;
23 import com.intellij.codeInspection.ex.GlobalInspectionContextBase;
24 import com.intellij.ide.util.MemberChooser;
25 import com.intellij.lang.ContextAwareActionHandler;
26 import com.intellij.openapi.actionSystem.DataContext;
27 import com.intellij.openapi.application.ApplicationManager;
28 import com.intellij.openapi.application.WriteAction;
29 import com.intellij.openapi.command.CommandProcessor;
30 import com.intellij.openapi.command.WriteCommandAction;
31 import com.intellij.openapi.diagnostic.Logger;
32 import com.intellij.openapi.editor.*;
33 import com.intellij.openapi.fileEditor.FileDocumentManager;
34 import com.intellij.openapi.project.Project;
35 import com.intellij.openapi.util.TextRange;
36 import com.intellij.openapi.util.text.StringUtil;
37 import com.intellij.psi.*;
38 import com.intellij.util.IncorrectOperationException;
39 import com.intellij.util.containers.ContainerUtil;
40 import org.jetbrains.annotations.NotNull;
41 import org.jetbrains.annotations.Nullable;
42 import org.jetbrains.java.generate.exception.GenerateCodeException;
43
44 import javax.swing.*;
45 import java.util.ArrayList;
46 import java.util.List;
47 import java.util.Objects;
48
49 public abstract class GenerateMembersHandlerBase implements CodeInsightActionHandler, ContextAwareActionHandler {
50   private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.generation.GenerateMembersHandlerBase");
51
52   private final String myChooserTitle;
53   protected boolean myToCopyJavaDoc;
54
55   public GenerateMembersHandlerBase(String chooserTitle) {
56     myChooserTitle = chooserTitle;
57   }
58
59   @Override
60   public boolean isAvailableForQuickList(@NotNull Editor editor, @NotNull PsiFile file, @NotNull DataContext dataContext) {
61     final PsiClass aClass = OverrideImplementUtil.getContextClass(file.getProject(), editor, file, false);
62     return aClass != null && hasMembers(aClass);
63   }
64
65   protected boolean hasMembers(@NotNull PsiClass aClass) {
66     return true;
67   }
68
69   @Override
70   public final void invoke(@NotNull final Project project, @NotNull final Editor editor, @NotNull PsiFile file) {
71     if (!EditorModificationUtil.checkModificationAllowed(editor)) return;
72     if (!FileDocumentManager.getInstance().requestWriting(editor.getDocument(), project)) {
73       return;
74     }
75     final PsiClass aClass = OverrideImplementUtil.getContextClass(project, editor, file, false);
76     if (aClass == null || aClass.isInterface()) return; //?
77     LOG.assertTrue(aClass.isValid());
78     LOG.assertTrue(aClass.getContainingFile() != null);
79
80     try {
81       final ClassMember[] members = chooseOriginalMembers(aClass, project, editor);
82       if (members == null) return;
83
84       CommandProcessor.getInstance().executeCommand(project, () -> {
85         final int offset = editor.getCaretModel().getOffset();
86         try {
87           doGenerate(project, editor, aClass, members);
88         }
89         catch (GenerateCodeException e) {
90           final String message = e.getMessage();
91           ApplicationManager.getApplication().invokeLater(() -> {
92             if (!editor.isDisposed()) {
93               editor.getCaretModel().moveToOffset(offset);
94               HintManager.getInstance().showErrorHint(editor, message);
95             }
96           }, project.getDisposed());
97         }
98       }, null, null);
99     }
100     finally {
101       cleanup();
102     }
103   }
104
105   protected void cleanup() {
106   }
107
108   private void doGenerate(final Project project, final Editor editor, PsiClass aClass, ClassMember[] members) {
109     int offset = editor.getCaretModel().getOffset();
110
111     int col = editor.getCaretModel().getLogicalPosition().column;
112     int line = editor.getCaretModel().getLogicalPosition().line;
113     final Document document = editor.getDocument();
114     int lineStartOffset = document.getLineStartOffset(line);
115     CharSequence docText = document.getCharsSequence();
116     String textBeforeCaret = docText.subSequence(lineStartOffset, offset).toString();
117     final String afterCaret = docText.subSequence(offset, document.getLineEndOffset(line)).toString();
118     final PsiElement lBrace = aClass.getLBrace();
119     if (textBeforeCaret.trim().length() > 0 && StringUtil.isEmptyOrSpaces(afterCaret) &&
120         (lBrace == null || lBrace.getTextOffset() < offset) && !editor.getSelectionModel().hasSelection()) {
121       PsiDocumentManager.getInstance(project).commitDocument(document);
122       offset = editor.getCaretModel().getOffset();
123       col = editor.getCaretModel().getLogicalPosition().column;
124       line = editor.getCaretModel().getLogicalPosition().line;
125     }
126
127     editor.getCaretModel().moveToLogicalPosition(new LogicalPosition(0, 0));
128
129     int finalOffset = offset;
130     List<? extends GenerationInfo> newMembers = WriteAction.compute(
131       () -> GenerateMembersUtil.insertMembersAtOffset(aClass, finalOffset, generateMemberPrototypes(aClass, members)));
132
133     editor.getCaretModel().moveToLogicalPosition(new LogicalPosition(line, col));
134
135     if (newMembers.isEmpty()) {
136       if (!ApplicationManager.getApplication().isUnitTestMode()) {
137         HintManager.getInstance().showErrorHint(editor, getNothingFoundMessage());
138       }
139       return;
140     } 
141     else {
142       final List<PsiElement> elements = new ArrayList<>();
143       for (GenerationInfo member : newMembers) {
144         if (!(member instanceof TemplateGenerationInfo)) {
145           ContainerUtil.addIfNotNull(elements, member.getPsiMember());
146         }
147       }
148
149       GlobalInspectionContextBase.cleanupElements(project, null, elements.toArray(PsiElement.EMPTY_ARRAY));
150     }
151
152     final ArrayList<TemplateGenerationInfo> templates = new ArrayList<>();
153     for (GenerationInfo member : newMembers) {
154       if (member instanceof TemplateGenerationInfo) {
155         templates.add((TemplateGenerationInfo) member);
156       }
157     }
158
159     if (!templates.isEmpty()){
160       runTemplates(project, editor, templates, 0);
161     }
162     else if (!newMembers.isEmpty()){
163       notifyOnSuccess(editor, members, newMembers);
164     }
165   }
166
167   protected void notifyOnSuccess(Editor editor,
168                                  ClassMember[] members,
169                                  List<? extends GenerationInfo> generatedMembers) {
170     generatedMembers.get(0).positionCaret(editor, false);
171   }
172
173   protected String getNothingFoundMessage() {
174     return "Nothing found to insert";
175   }
176
177   private static void runTemplates(final Project myProject, final Editor editor, final List<? extends TemplateGenerationInfo> templates, final int index) {
178     TemplateGenerationInfo info = templates.get(index);
179     final Template template = info.getTemplate();
180
181     PsiElement element = Objects.requireNonNull(info.getPsiMember());
182     final TextRange range = element.getTextRange();
183     WriteAction.run(() -> editor.getDocument().deleteString(range.getStartOffset(), range.getEndOffset()));
184     int offset = range.getStartOffset();
185     editor.getCaretModel().moveToOffset(offset);
186     editor.getScrollingModel().scrollToCaret(ScrollType.CENTER);
187     TemplateManager.getInstance(myProject).startTemplate(editor, template, new TemplateEditingAdapter() {
188       @Override
189       public void templateFinished(@NotNull Template template, boolean brokenOff) {
190         if (index + 1 < templates.size()){
191           ApplicationManager.getApplication().invokeLater(() -> WriteCommandAction.runWriteCommandAction(myProject, ()->
192               runTemplates(myProject, editor, templates, index + 1)
193           ));
194         }
195       }
196     });
197   }
198
199
200   @Nullable
201   protected ClassMember[] chooseOriginalMembers(PsiClass aClass, Project project) {
202     ClassMember[] allMembers = getAllOriginalMembers(aClass);
203     return chooseMembers(allMembers, false, false, project, null);
204   }
205
206   @Nullable
207   protected ClassMember[] chooseOriginalMembers(PsiClass aClass, Project project, Editor editor) {
208     return chooseOriginalMembers(aClass, project);
209   }
210
211   @Nullable
212   protected ClassMember[] chooseMembers(ClassMember[] members,
213                                         boolean allowEmptySelection,
214                                         boolean copyJavadocCheckbox,
215                                         Project project,
216                                         @Nullable Editor editor) {
217     MemberChooser<ClassMember> chooser = createMembersChooser(members, allowEmptySelection, copyJavadocCheckbox, project);
218     if (editor != null) {
219       final int offset = editor.getCaretModel().getOffset();
220
221       ClassMember preselection = null;
222       for (ClassMember member : members) {
223         if (member instanceof PsiElementClassMember) {
224           final PsiDocCommentOwner owner = ((PsiElementClassMember)member).getElement();
225           if (owner != null) {
226             final TextRange textRange = owner.getTextRange();
227             if (textRange != null && textRange.contains(offset)) {
228               preselection = member;
229               break;
230             }
231           }
232         }
233       }
234       if (preselection != null) {
235         chooser.selectElements(new ClassMember[]{preselection});
236       }
237     }
238
239     chooser.show();
240     myToCopyJavaDoc = chooser.isCopyJavadoc();
241     final List<ClassMember> list = chooser.getSelectedElements();
242     return list == null ? null : list.toArray(ClassMember.EMPTY_ARRAY);
243   }
244
245   protected MemberChooser<ClassMember> createMembersChooser(ClassMember[] members,
246                                                             boolean allowEmptySelection,
247                                                             boolean copyJavadocCheckbox,
248                                                             Project project) {
249     MemberChooser<ClassMember> chooser = new MemberChooser<ClassMember>(members, allowEmptySelection, true, project, getHeaderPanel(project), getOptionControls()) {
250       @Nullable
251       @Override
252       protected String getHelpId() {
253         return GenerateMembersHandlerBase.this.getHelpId();
254       }
255     };
256     chooser.setTitle(myChooserTitle);
257     chooser.setCopyJavadocVisible(copyJavadocCheckbox);
258     return chooser;
259   }
260
261   @Nullable
262   protected JComponent getHeaderPanel(Project project) {
263     return null;
264   }
265
266   @Nullable
267   protected JComponent[] getOptionControls() {
268     return null;
269   }
270
271   protected String getHelpId() {
272     return null;
273   }
274
275   @NotNull
276   protected List<? extends GenerationInfo> generateMemberPrototypes(PsiClass aClass, ClassMember[] members) throws IncorrectOperationException {
277     ArrayList<GenerationInfo> array = new ArrayList<>();
278     for (ClassMember member : members) {
279       GenerationInfo[] prototypes = generateMemberPrototypes(aClass, member);
280       if (prototypes != null) {
281         ContainerUtil.addAll(array, prototypes);
282       }
283     }
284     return array;
285   }
286
287   protected abstract ClassMember[] getAllOriginalMembers(PsiClass aClass);
288
289   protected abstract GenerationInfo[] generateMemberPrototypes(PsiClass aClass, ClassMember originalMember) throws IncorrectOperationException;
290
291   @Override
292   public boolean startInWriteAction() {
293     return false;
294   }
295 }