| /* |
| * 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.daemon.impl.quickfix; |
| |
| import com.intellij.codeInsight.CodeInsightUtilCore; |
| import com.intellij.codeInsight.ExpectedTypeInfo; |
| import com.intellij.codeInsight.ExpectedTypesProvider; |
| import com.intellij.codeInsight.daemon.QuickFixBundle; |
| import com.intellij.codeInsight.template.Template; |
| import com.intellij.codeInsight.template.TemplateBuilderImpl; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.application.Result; |
| import com.intellij.openapi.command.CommandProcessor; |
| import com.intellij.openapi.command.WriteCommandAction; |
| import com.intellij.openapi.editor.Editor; |
| import com.intellij.openapi.editor.RangeMarker; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.psi.*; |
| import com.intellij.psi.util.PsiTreeUtil; |
| import com.intellij.util.IncorrectOperationException; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| /** |
| * @author mike |
| */ |
| public class CreateClassFromNewFix extends CreateFromUsageBaseFix { |
| private final SmartPsiElementPointer myNewExpression; |
| |
| public CreateClassFromNewFix(PsiNewExpression newExpression) { |
| myNewExpression = SmartPointerManager.getInstance(newExpression.getProject()).createSmartPsiElementPointer(newExpression); |
| } |
| |
| protected PsiNewExpression getNewExpression() { |
| return (PsiNewExpression)myNewExpression.getElement(); |
| } |
| |
| @Override |
| protected void invokeImpl(PsiClass targetClass) { |
| assert ApplicationManager.getApplication().isWriteAccessAllowed(); |
| |
| final PsiNewExpression newExpression = getNewExpression(); |
| |
| final PsiJavaCodeReferenceElement referenceElement = getReferenceElement(newExpression); |
| ApplicationManager.getApplication().invokeLater(new Runnable() { |
| @Override |
| public void run() { |
| final PsiClass[] psiClass = new PsiClass[1]; |
| CommandProcessor.getInstance().executeCommand(newExpression.getProject(), new Runnable() { |
| @Override |
| public void run() { |
| psiClass[0] = CreateFromUsageUtils.createClass(referenceElement, CreateClassKind.CLASS, null); |
| } |
| }, getText(), getText()); |
| new WriteCommandAction(newExpression.getProject(), getText(), getText()) { |
| @Override |
| protected void run(Result result) throws Throwable { |
| setupClassFromNewExpression(psiClass[0], newExpression); |
| } |
| }.execute(); |
| } |
| }); |
| } |
| |
| protected void setupClassFromNewExpression(final PsiClass psiClass, final PsiNewExpression newExpression) { |
| assert ApplicationManager.getApplication().isWriteAccessAllowed(); |
| |
| final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(newExpression.getProject()).getElementFactory(); |
| PsiClass aClass = psiClass; |
| if (aClass == null) return; |
| |
| final PsiJavaCodeReferenceElement classReference = newExpression.getClassReference(); |
| if (classReference != null) { |
| classReference.bindToElement(aClass); |
| } |
| setupInheritance(newExpression, aClass); |
| |
| PsiExpressionList argList = newExpression.getArgumentList(); |
| final Project project = aClass.getProject(); |
| if (argList != null && argList.getExpressions().length > 0) { |
| PsiMethod constructor = elementFactory.createConstructor(); |
| constructor = (PsiMethod)aClass.add(constructor); |
| |
| TemplateBuilderImpl templateBuilder = new TemplateBuilderImpl(aClass); |
| CreateFromUsageUtils.setupMethodParameters(constructor, templateBuilder, argList, getTargetSubstitutor(newExpression)); |
| |
| setupSuperCall(aClass, constructor, templateBuilder); |
| |
| getReferenceElement(newExpression).bindToElement(aClass); |
| aClass = CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(aClass); |
| final Template template = templateBuilder.buildTemplate(); |
| template.setToReformat(true); |
| |
| final Editor editor = positionCursor(project, aClass.getContainingFile(), aClass); |
| if (editor == null) return; |
| final RangeMarker textRange = editor.getDocument().createRangeMarker(aClass.getTextRange()); |
| final Runnable runnable = new Runnable() { |
| @Override |
| public void run() { |
| new WriteCommandAction(project, getText(), getText()) { |
| @Override |
| protected void run(Result result) throws Throwable { |
| try { |
| editor.getDocument().deleteString(textRange.getStartOffset(), textRange.getEndOffset()); |
| } |
| finally { |
| textRange.dispose(); |
| } |
| } |
| }.execute(); |
| startTemplate(editor, template, project, null, getText()); |
| } |
| }; |
| if (ApplicationManager.getApplication().isUnitTestMode()) { |
| runnable.run(); |
| } |
| else { |
| ApplicationManager.getApplication().invokeLater(runnable); |
| } |
| } |
| else { |
| positionCursor(project, aClass.getContainingFile(), aClass); |
| } |
| } |
| |
| @Nullable |
| public static PsiMethod setupSuperCall(PsiClass targetClass, PsiMethod constructor, TemplateBuilderImpl templateBuilder) |
| throws IncorrectOperationException { |
| PsiElementFactory elementFactory = JavaPsiFacade.getInstance(targetClass.getProject()).getElementFactory(); |
| PsiMethod supConstructor = null; |
| PsiClass superClass = targetClass.getSuperClass(); |
| if (superClass != null && !CommonClassNames.JAVA_LANG_OBJECT.equals(superClass.getQualifiedName()) && |
| !"java.lang.Enum".equals(superClass.getQualifiedName())) { |
| PsiMethod[] constructors = superClass.getConstructors(); |
| boolean hasDefaultConstructor = false; |
| |
| for (PsiMethod superConstructor : constructors) { |
| if (superConstructor.getParameterList().getParametersCount() == 0) { |
| hasDefaultConstructor = true; |
| supConstructor = null; |
| break; |
| } |
| else { |
| supConstructor = superConstructor; |
| } |
| } |
| |
| if (!hasDefaultConstructor) { |
| PsiExpressionStatement statement = |
| (PsiExpressionStatement)elementFactory.createStatementFromText("super();", constructor); |
| statement = (PsiExpressionStatement)constructor.getBody().add(statement); |
| |
| PsiMethodCallExpression call = (PsiMethodCallExpression)statement.getExpression(); |
| PsiExpressionList argumentList = call.getArgumentList(); |
| templateBuilder.setEndVariableAfter(argumentList.getFirstChild()); |
| return supConstructor; |
| } |
| } |
| |
| templateBuilder.setEndVariableAfter(constructor.getBody().getLBrace()); |
| return supConstructor; |
| } |
| |
| private static void setupInheritance(PsiNewExpression element, PsiClass targetClass) throws IncorrectOperationException { |
| if (element.getParent() instanceof PsiReferenceExpression) return; |
| |
| ExpectedTypeInfo[] expectedTypes = ExpectedTypesProvider.getExpectedTypes(element, false); |
| |
| for (ExpectedTypeInfo expectedType : expectedTypes) { |
| PsiType type = expectedType.getType(); |
| if (!(type instanceof PsiClassType)) continue; |
| final PsiClassType classType = (PsiClassType)type; |
| PsiClass aClass = classType.resolve(); |
| if (aClass == null) continue; |
| if (aClass.equals(targetClass) || aClass.hasModifierProperty(PsiModifier.FINAL)) continue; |
| PsiElementFactory factory = JavaPsiFacade.getInstance(aClass.getProject()).getElementFactory(); |
| |
| if (aClass.isInterface()) { |
| PsiReferenceList implementsList = targetClass.getImplementsList(); |
| assert implementsList != null : targetClass; |
| implementsList.add(factory.createReferenceElementByType(classType)); |
| } |
| else { |
| PsiReferenceList extendsList = targetClass.getExtendsList(); |
| assert extendsList != null : targetClass; |
| if (extendsList.getReferencedTypes().length == 0 && !CommonClassNames.JAVA_LANG_OBJECT.equals(classType.getCanonicalText())) { |
| extendsList.add(factory.createReferenceElementByType(classType)); |
| } |
| } |
| } |
| } |
| |
| private static PsiFile getTargetFile(PsiElement element) { |
| PsiJavaCodeReferenceElement referenceElement = getReferenceElement((PsiNewExpression)element); |
| |
| PsiElement q = referenceElement.getQualifier(); |
| if (q instanceof PsiJavaCodeReferenceElement) { |
| PsiJavaCodeReferenceElement qualifier = (PsiJavaCodeReferenceElement)q; |
| PsiElement psiElement = qualifier.resolve(); |
| if (psiElement instanceof PsiClass) { |
| PsiClass psiClass = (PsiClass)psiElement; |
| return psiClass.getContainingFile(); |
| } |
| } |
| |
| return null; |
| } |
| |
| @Override |
| protected PsiElement getElement() { |
| final PsiNewExpression expression = getNewExpression(); |
| if (expression == null || !expression.getManager().isInProject(expression)) return null; |
| PsiJavaCodeReferenceElement referenceElement = getReferenceElement(expression); |
| if (referenceElement == null) return null; |
| if (referenceElement.getReferenceNameElement() instanceof PsiIdentifier) return expression; |
| |
| return null; |
| } |
| |
| @Override |
| protected boolean isAllowOuterTargetClass() { |
| return false; |
| } |
| |
| @Override |
| protected boolean isValidElement(PsiElement element) { |
| PsiJavaCodeReferenceElement ref = PsiTreeUtil.getChildOfType(element, PsiJavaCodeReferenceElement.class); |
| return ref != null && ref.resolve() != null; |
| } |
| |
| @Override |
| protected boolean isAvailableImpl(int offset) { |
| PsiNewExpression expression = getNewExpression(); |
| if (expression.getQualifier() != null) { |
| return false; |
| } |
| |
| PsiFile targetFile = getTargetFile(expression); |
| if (targetFile != null && !targetFile.getManager().isInProject(targetFile)) { |
| return false; |
| } |
| |
| PsiElement nameElement = getNameElement(expression); |
| if (CreateFromUsageUtils.shouldShowTag(offset, nameElement, expression)) { |
| String varName = nameElement.getText(); |
| setText(getText(varName)); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| protected String getText(final String varName) { |
| return QuickFixBundle.message("create.class.from.new.text", varName); |
| } |
| |
| protected static PsiJavaCodeReferenceElement getReferenceElement(PsiNewExpression expression) { |
| return expression.getClassOrAnonymousClassReference(); |
| } |
| |
| private static PsiElement getNameElement(PsiNewExpression targetElement) { |
| PsiJavaCodeReferenceElement referenceElement = getReferenceElement(targetElement); |
| return referenceElement != null ? referenceElement.getReferenceNameElement() : null; |
| } |
| |
| @Override |
| @NotNull |
| public String getFamilyName() { |
| return QuickFixBundle.message("create.class.from.new.family"); |
| } |
| |
| @Override |
| protected boolean canBeTargetClass(PsiClass psiClass) { |
| return false; |
| } |
| } |