/* * Copyright 2000-2013 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.intellij.codeInsight.generation; import com.intellij.analysis.AnalysisScope; import com.intellij.codeInsight.CodeInsightActionHandler; import com.intellij.codeInsight.CodeInsightUtilBase; import com.intellij.codeInsight.hint.HintManager; import com.intellij.codeInsight.template.Template; import com.intellij.codeInsight.template.TemplateEditingAdapter; import com.intellij.codeInsight.template.TemplateManager; import com.intellij.codeInspection.ex.GlobalInspectionContextBase; import com.intellij.ide.util.MemberChooser; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.Result; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.LogicalPosition; import com.intellij.openapi.editor.ScrollType; import com.intellij.openapi.editor.actions.EnterAction; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.search.LocalSearchScope; import com.intellij.util.Function; import com.intellij.util.IncorrectOperationException; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.util.ArrayList; import java.util.List; public abstract class GenerateMembersHandlerBase implements CodeInsightActionHandler { private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.generation.GenerateMembersHandlerBase"); private final String myChooserTitle; protected boolean myToCopyJavaDoc = false; public GenerateMembersHandlerBase(String chooserTitle) { myChooserTitle = chooserTitle; } @Override public final void invoke(@NotNull final Project project, @NotNull final Editor editor, @NotNull PsiFile file) { if (!CodeInsightUtilBase.prepareEditorForWrite(editor)) return; if (!FileDocumentManager.getInstance().requestWriting(editor.getDocument(), project)) { return; } final PsiClass aClass = OverrideImplementUtil.getContextClass(project, editor, file, false); if (aClass == null || aClass.isInterface()) return; //? LOG.assertTrue(aClass.isValid()); LOG.assertTrue(aClass.getContainingFile() != null); try { final ClassMember[] members = chooseOriginalMembers(aClass, project, editor); if (members == null) return; WriteCommandAction.runWriteCommandAction(project, new Runnable() { @Override public void run() { doGenerate(project, editor, aClass, members); } }); } finally { cleanup(); } } protected void cleanup() { } private void doGenerate(final Project project, final Editor editor, PsiClass aClass, ClassMember[] members) { int offset = editor.getCaretModel().getOffset(); int col = editor.getCaretModel().getLogicalPosition().column; int line = editor.getCaretModel().getLogicalPosition().line; final Document document = editor.getDocument(); int lineStartOffset = document.getLineStartOffset(line); CharSequence docText = document.getCharsSequence(); String textBeforeCaret = docText.subSequence(lineStartOffset, offset).toString(); final String afterCaret = docText.subSequence(offset, document.getLineEndOffset(line)).toString(); if (textBeforeCaret.trim().length() > 0 && StringUtil.isEmptyOrSpaces(afterCaret) && !editor.getSelectionModel().hasSelection()) { EnterAction.insertNewLineAtCaret(editor); PsiDocumentManager.getInstance(project).commitDocument(document); offset = editor.getCaretModel().getOffset(); col = editor.getCaretModel().getLogicalPosition().column; line = editor.getCaretModel().getLogicalPosition().line; } editor.getCaretModel().moveToLogicalPosition(new LogicalPosition(0, 0)); List newMembers; try{ List prototypes = generateMemberPrototypes(aClass, members); newMembers = GenerateMembersUtil.insertMembersAtOffset(aClass.getContainingFile(), offset, prototypes); } catch(IncorrectOperationException e){ LOG.error(e); return; } editor.getCaretModel().moveToLogicalPosition(new LogicalPosition(line, col)); if (newMembers.isEmpty()) { if (!ApplicationManager.getApplication().isUnitTestMode()) { HintManager.getInstance().showErrorHint(editor, getNothingFoundMessage()); } return; } else { final List elements = new ArrayList(); for (GenerationInfo member : newMembers) { if (!(member instanceof TemplateGenerationInfo)) { final PsiMember psiMember = member.getPsiMember(); if (psiMember != null) { elements.add(psiMember); } } } GlobalInspectionContextBase.cleanupElements(project, null, elements.toArray(new PsiElement[elements.size()])); } final ArrayList templates = new ArrayList(); for (GenerationInfo member : newMembers) { if (member instanceof TemplateGenerationInfo) { templates.add((TemplateGenerationInfo) member); } } if (!templates.isEmpty()){ PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(document); runTemplates(project, editor, templates, 0); } else if (!newMembers.isEmpty()){ newMembers.get(0).positionCaret(editor, false); } } protected String getNothingFoundMessage() { return "Nothing found to insert"; } private static void runTemplates(final Project myProject, final Editor editor, final List templates, final int index) { TemplateGenerationInfo info = templates.get(index); final Template template = info.getTemplate(); final PsiElement element = info.getPsiMember(); final TextRange range = element.getTextRange(); editor.getDocument().deleteString(range.getStartOffset(), range.getEndOffset()); int offset = range.getStartOffset(); editor.getCaretModel().moveToOffset(offset); editor.getScrollingModel().scrollToCaret(ScrollType.CENTER); TemplateManager.getInstance(myProject).startTemplate(editor, template, new TemplateEditingAdapter() { @Override public void templateFinished(Template template, boolean brokenOff) { if (index + 1 < templates.size()){ ApplicationManager.getApplication().invokeLater(new Runnable() { @Override public void run() { new WriteCommandAction(myProject) { @Override protected void run(Result result) throws Throwable { runTemplates(myProject, editor, templates, index + 1); } }.execute(); } }); } } }); } @Nullable protected ClassMember[] chooseOriginalMembers(PsiClass aClass, Project project) { ClassMember[] allMembers = getAllOriginalMembers(aClass); return chooseMembers(allMembers, false, false, project, null); } @Nullable protected ClassMember[] chooseOriginalMembers(PsiClass aClass, Project project, Editor editor) { return chooseOriginalMembers(aClass, project); } @Nullable protected ClassMember[] chooseMembers(ClassMember[] members, boolean allowEmptySelection, boolean copyJavadocCheckbox, Project project, @Nullable Editor editor) { MemberChooser chooser = createMembersChooser(members, allowEmptySelection, copyJavadocCheckbox, project); if (editor != null) { final int offset = editor.getCaretModel().getOffset(); ClassMember preselection = null; for (ClassMember member : members) { if (member instanceof PsiElementClassMember) { final PsiDocCommentOwner owner = ((PsiElementClassMember)member).getElement(); if (owner != null) { final TextRange textRange = owner.getTextRange(); if (textRange != null && textRange.contains(offset)) { preselection = member; break; } } } } if (preselection != null) { chooser.selectElements(new ClassMember[]{preselection}); } } chooser.show(); myToCopyJavaDoc = chooser.isCopyJavadoc(); final List list = chooser.getSelectedElements(); return list == null ? null : list.toArray(new ClassMember[list.size()]); } protected MemberChooser createMembersChooser(ClassMember[] members, boolean allowEmptySelection, boolean copyJavadocCheckbox, Project project) { MemberChooser chooser = new MemberChooser(members, allowEmptySelection, true, project); chooser.setTitle(myChooserTitle); chooser.setCopyJavadocVisible(copyJavadocCheckbox); return chooser; } @NotNull protected List generateMemberPrototypes(PsiClass aClass, ClassMember[] members) throws IncorrectOperationException { ArrayList array = new ArrayList(); for (ClassMember member : members) { GenerationInfo[] prototypes = generateMemberPrototypes(aClass, member); if (prototypes != null) { ContainerUtil.addAll(array, prototypes); } } return array; } protected abstract ClassMember[] getAllOriginalMembers(PsiClass aClass); protected abstract GenerationInfo[] generateMemberPrototypes(PsiClass aClass, ClassMember originalMember) throws IncorrectOperationException; @Override public boolean startInWriteAction() { return false; } }