2 * Copyright 2000-2009 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 * Created by IntelliJ IDEA.
22 * To change template for new class use
23 * Code Style | Class Templates options (Tools | IDE Options).
25 package com
.intellij
.codeInsight
.intention
.impl
;
27 import com
.intellij
.codeInsight
.CodeInsightBundle
;
28 import com
.intellij
.codeInsight
.CodeInsightUtil
;
29 import com
.intellij
.codeInsight
.daemon
.impl
.analysis
.HighlightNamesUtil
;
30 import com
.intellij
.codeInsight
.daemon
.impl
.quickfix
.CreateClassKind
;
31 import com
.intellij
.codeInsight
.generation
.OverrideImplementUtil
;
32 import com
.intellij
.codeInsight
.intention
.PsiElementBaseIntentionAction
;
33 import com
.intellij
.openapi
.application
.ApplicationManager
;
34 import com
.intellij
.openapi
.diagnostic
.Logger
;
35 import com
.intellij
.openapi
.editor
.Editor
;
36 import com
.intellij
.openapi
.fileEditor
.ex
.IdeDocumentHistory
;
37 import com
.intellij
.openapi
.module
.ModuleUtil
;
38 import com
.intellij
.openapi
.project
.Project
;
39 import com
.intellij
.openapi
.ui
.Messages
;
40 import com
.intellij
.openapi
.util
.Computable
;
41 import com
.intellij
.openapi
.util
.TextRange
;
42 import com
.intellij
.psi
.*;
43 import com
.intellij
.psi
.impl
.source
.PostprocessReformattingAspect
;
44 import com
.intellij
.psi
.util
.PsiTreeUtil
;
45 import com
.intellij
.util
.IncorrectOperationException
;
46 import org
.jetbrains
.annotations
.NonNls
;
47 import org
.jetbrains
.annotations
.NotNull
;
48 import org
.jetbrains
.annotations
.Nullable
;
50 public class CreateSubclassAction
extends PsiElementBaseIntentionAction
{
51 private static final Logger LOG
= Logger
.getInstance("#com.intellij.codeInsight.intention.impl.ImplementAbstractClassAction");
52 private String myText
= CodeInsightBundle
.message("intention.implement.abstract.class.default.text");
53 @NonNls private static final String IMPL_SUFFIX
= "Impl";
56 public String
getText() {
61 public String
getFamilyName() {
62 return CodeInsightBundle
.message("intention.implement.abstract.class.family");
65 public boolean isAvailable(@NotNull Project project
, Editor editor
, @Nullable PsiElement element
) {
66 if (element
== null) return false;
67 PsiClass psiClass
= PsiTreeUtil
.getParentOfType(element
, PsiClass
.class);
68 if (psiClass
== null || psiClass
.isAnnotationType() || psiClass
.isEnum() || psiClass
instanceof PsiAnonymousClass
||
69 psiClass
.hasModifierProperty(PsiModifier
.FINAL
)) {
72 final PsiMethod
[] constructors
= psiClass
.getConstructors();
73 if (constructors
.length
> 0) {
74 boolean hasNonPrivateConstructor
= false;
75 for (PsiMethod constructor
: constructors
) {
76 if (!constructor
.hasModifierProperty(PsiModifier
.PRIVATE
)) {
77 hasNonPrivateConstructor
= true;
81 if (!hasNonPrivateConstructor
) return false;
83 PsiJavaToken lBrace
= psiClass
.getLBrace();
84 if (lBrace
== null) return false;
85 if (element
.getTextOffset() >= lBrace
.getTextOffset()) return false;
87 TextRange declarationRange
= HighlightNamesUtil
.getClassDeclarationTextRange(psiClass
);
88 if (!declarationRange
.contains(element
.getTextRange())) return false;
90 myText
= psiClass
.isInterface()
91 ? CodeInsightBundle
.message("intention.implement.abstract.class.interface.text")
92 : psiClass
.hasModifierProperty(PsiModifier
.ABSTRACT
)
93 ? CodeInsightBundle
.message("intention.implement.abstract.class.default.text")
94 : CodeInsightBundle
.message("intention.implement.abstract.class.subclass.text");
98 public void invoke(@NotNull final Project project
, Editor editor
, final PsiFile file
) throws IncorrectOperationException
{
99 PsiElement element
= file
.findElementAt(editor
.getCaretModel().getOffset());
100 final PsiClass psiClass
= PsiTreeUtil
.getParentOfType(element
, PsiClass
.class);
102 PsiDirectory sourceDir
= file
.getContainingDirectory();
104 final PsiPackage aPackage
= JavaDirectoryService
.getInstance().getPackage(sourceDir
);
105 final CreateClassDialog dialog
= new CreateClassDialog(
108 psiClass
.getName() + IMPL_SUFFIX
,
109 aPackage
!= null ? aPackage
.getQualifiedName() : "",
110 CreateClassKind
.CLASS
, true, ModuleUtil
.findModuleForPsiElement(file
));
112 if (!dialog
.isOK()) return;
113 final PsiDirectory targetDirectory
= dialog
.getTargetDirectory();
114 if (targetDirectory
== null) return;
116 createSubclass(psiClass
, targetDirectory
, dialog
.getClassName());
119 public static void createSubclass(final PsiClass psiClass
, final PsiDirectory targetDirectory
, final String className
) {
120 final Project project
= psiClass
.getProject();
121 PostprocessReformattingAspect
.getInstance(project
).postponeFormattingInside(new Runnable () {
123 PsiClass targetClass
= ApplicationManager
.getApplication().runWriteAction(new Computable
<PsiClass
>() {
124 public PsiClass
compute() {
126 IdeDocumentHistory
.getInstance(project
).includeCurrentPlaceAsChangePlace();
128 final PsiTypeParameterList oldTypeParameterList
= psiClass
.getTypeParameterList();
129 PsiClass targetClass
;
131 targetClass
= JavaDirectoryService
.getInstance().createClass(targetDirectory
, className
);
132 if (psiClass
.hasTypeParameters()) {
133 final PsiTypeParameterList typeParameterList
= targetClass
.getTypeParameterList();
134 assert typeParameterList
!= null;
135 typeParameterList
.replace(oldTypeParameterList
);
138 catch (final IncorrectOperationException e
) {
139 ApplicationManager
.getApplication().invokeLater(new Runnable() {
141 Messages
.showErrorDialog(project
, CodeInsightBundle
.message("intention.error.cannot.create.class.message", className
) +
142 "\n"+e
.getLocalizedMessage(),
143 CodeInsightBundle
.message("intention.error.cannot.create.class.title"));
148 final PsiElementFactory elementFactory
= JavaPsiFacade
.getInstance(project
).getElementFactory();
149 PsiJavaCodeReferenceElement ref
= elementFactory
.createClassReferenceElement(psiClass
);
151 if (psiClass
.isInterface()) {
152 ref
= (PsiJavaCodeReferenceElement
)targetClass
.getImplementsList().add(ref
);
155 ref
= (PsiJavaCodeReferenceElement
)targetClass
.getExtendsList().add(ref
);
158 if (oldTypeParameterList
!= null) {
159 for (PsiTypeParameter parameter
: oldTypeParameterList
.getTypeParameters()) {
160 ref
.getParameterList().add(elementFactory
.createTypeElement(elementFactory
.createType(parameter
)));
164 catch (IncorrectOperationException e
) {
171 if (targetClass
== null) return;
173 if (!ApplicationManager
.getApplication().isUnitTestMode()) {
174 final Editor editor1
= CodeInsightUtil
.positionCursor(project
, targetClass
.getContainingFile(), targetClass
.getLBrace());
175 if (editor1
== null) return;
176 OverrideImplementUtil
.chooseAndImplementMethods(project
, editor1
, targetClass
);
182 public boolean startInWriteAction() {