IDEA-136406 Complete current statement doesn't insert a method body for default metho...
[idea/community.git] / java / java-impl / src / com / intellij / codeInsight / editorActions / smartEnter / SemicolonFixer.java
1 /*
2  * Copyright 2000-2009 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.editorActions.smartEnter;
17
18 import com.intellij.lang.ASTNode;
19 import com.intellij.openapi.editor.Document;
20 import com.intellij.openapi.editor.Editor;
21 import com.intellij.openapi.project.Project;
22 import com.intellij.openapi.util.TextRange;
23 import com.intellij.openapi.util.text.StringUtil;
24 import com.intellij.psi.*;
25 import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
26 import com.intellij.psi.impl.source.tree.ElementType;
27 import com.intellij.psi.impl.source.tree.TreeUtil;
28 import com.intellij.psi.util.PsiTreeUtil;
29 import com.intellij.util.IncorrectOperationException;
30 import org.jetbrains.annotations.NotNull;
31 import org.jetbrains.annotations.Nullable;
32
33 /**
34  * Created by IntelliJ IDEA.
35  * User: max
36  * Date: Sep 5, 2003
37  * Time: 3:35:49 PM
38  * To change this template use Options | File Templates.
39  */
40 @SuppressWarnings({"HardCodedStringLiteral"})
41 public class SemicolonFixer implements Fixer {
42   @Override
43   public void apply(Editor editor, JavaSmartEnterProcessor processor, PsiElement psiElement) throws IncorrectOperationException {
44     boolean fixed = fixReturn(editor, psiElement) || fixForUpdate(editor, psiElement, processor)
45                     || fixAfterLastValidElement(editor, psiElement);
46   }
47
48   private static boolean fixReturn(@NotNull Editor editor, @Nullable PsiElement psiElement) {
49     if (psiElement instanceof PsiReturnStatement) {
50       PsiMethod method = PsiTreeUtil.getParentOfType(psiElement, PsiMethod.class, true, PsiLambdaExpression.class);
51       if (method != null && PsiType.VOID.equals(method.getReturnType())) {
52         PsiReturnStatement stmt = (PsiReturnStatement)psiElement;
53         if (stmt.getReturnValue() != null) {
54           Document doc = editor.getDocument();
55           doc.insertString(stmt.getTextRange().getStartOffset() + "return".length(), ";");
56           return true;
57         }
58       }
59     }
60     return false;
61   }
62
63   private static boolean fixForUpdate(@NotNull Editor editor, @Nullable PsiElement psiElement, @NotNull JavaSmartEnterProcessor processor) {
64     if (!(psiElement instanceof PsiForStatement)) {
65       return false;
66     }
67     
68     PsiForStatement forStatement = (PsiForStatement)psiElement;
69     final PsiExpression condition = forStatement.getCondition();
70     if (forStatement.getUpdate() != null || condition == null) {
71       return false;
72     }
73
74     final TextRange range = condition.getTextRange();
75     final Document document = editor.getDocument();
76     final CharSequence text = document.getCharsSequence();
77     for (int i = range.getEndOffset() - 1, max = forStatement.getTextRange().getEndOffset(); i < max; i++) {
78       if (text.charAt(i) == ';') {
79         return false;
80       }
81     }
82
83     String toInsert = ";";
84     final Project project = editor.getProject();
85     if (project != null && CodeStyleSettingsManager.getSettings(project).SPACE_AFTER_SEMICOLON) {
86       toInsert += " ";
87     }
88     document.insertString(range.getEndOffset(), toInsert);
89     return true;
90   }
91   
92   private static boolean fixAfterLastValidElement(@NotNull Editor editor, @Nullable PsiElement psiElement) {
93     if (psiElement == null ||
94         !(psiElement instanceof PsiExpressionStatement) &&
95         !(psiElement instanceof PsiDeclarationStatement) &&
96         !(psiElement instanceof PsiImportStatementBase) &&
97         !(psiElement instanceof PsiDoWhileStatement) &&
98         !(psiElement instanceof PsiReturnStatement) &&
99         !(psiElement instanceof PsiThrowStatement) &&
100         !(psiElement instanceof PsiBreakStatement) &&
101         !(psiElement instanceof PsiContinueStatement) &&
102         !(psiElement instanceof PsiAssertStatement) &&
103         !(psiElement instanceof PsiPackageStatement) &&
104         (!(psiElement instanceof PsiField) || psiElement instanceof PsiEnumConstant) &&
105         (!(psiElement instanceof PsiMethod) || ((PsiMethod)psiElement).getBody() != null || MissingMethodBodyFixer.shouldHaveBody((PsiMethod)psiElement))) {
106       return false;
107     }
108     String text = psiElement.getText();
109
110     int tailLength = 0;
111     ASTNode leaf = TreeUtil.findLastLeaf(psiElement.getNode());
112     while (leaf != null && ElementType.JAVA_COMMENT_OR_WHITESPACE_BIT_SET.contains(leaf.getElementType())) {
113       tailLength += leaf.getTextLength();
114       leaf = TreeUtil.prevLeaf(leaf);
115     }
116
117     if (tailLength > 0) {
118       text = text.substring(0, text.length() - tailLength);
119     }
120
121     if (leaf == null) {
122       return false;
123     }
124     int insertionOffset = leaf.getTextRange().getEndOffset();
125     Document doc = editor.getDocument();
126     if (psiElement instanceof PsiField && ((PsiField)psiElement).hasModifierProperty(PsiModifier.ABSTRACT)) {
127       // abstract rarely seem to be field. It is rather incomplete method.
128       doc.insertString(insertionOffset, "()");
129       insertionOffset += "()".length();
130     }
131
132     if (!StringUtil.endsWithChar(text, ';')) {
133       final PsiElement parent = psiElement.getParent();
134       String toInsert = ";";
135       if (parent instanceof PsiForStatement) {
136         if (((PsiForStatement)parent).getUpdate() == psiElement) {
137           return false;
138         }
139         else {
140           final Project project = editor.getProject();
141           if (project != null && CodeStyleSettingsManager.getSettings(project).SPACE_AFTER_SEMICOLON) {
142             toInsert += " ";
143           }
144         }
145       }
146
147       doc.insertString(insertionOffset, toInsert);
148       return true;
149     }
150     return false;
151   }
152 }