| /* |
| * 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.refactoring.extractclass; |
| |
| import com.intellij.codeInsight.generation.GenerateMembersUtil; |
| import com.intellij.ide.highlighter.JavaFileType; |
| import com.intellij.ide.util.PackageUtil; |
| 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.module.Module; |
| import com.intellij.openapi.module.ModuleUtil; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.util.Condition; |
| import com.intellij.openapi.util.Ref; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.psi.*; |
| import com.intellij.psi.codeStyle.CodeStyleManager; |
| import com.intellij.psi.codeStyle.CodeStyleSettings; |
| import com.intellij.psi.codeStyle.CodeStyleSettingsManager; |
| import com.intellij.psi.codeStyle.JavaCodeStyleManager; |
| import com.intellij.psi.javadoc.PsiDocTagValue; |
| import com.intellij.psi.search.GlobalSearchScope; |
| import com.intellij.psi.search.searches.ReferencesSearch; |
| import com.intellij.psi.tree.IElementType; |
| import com.intellij.psi.util.InheritanceUtil; |
| import com.intellij.psi.util.PropertyUtil; |
| import com.intellij.psi.util.PsiTreeUtil; |
| import com.intellij.refactoring.MoveDestination; |
| import com.intellij.refactoring.RefactorJBundle; |
| import com.intellij.refactoring.extractclass.usageInfo.*; |
| import com.intellij.refactoring.move.MoveInstanceMembersUtil; |
| import com.intellij.refactoring.move.moveClassesOrPackages.DestinationFolderComboBox; |
| import com.intellij.refactoring.psi.MethodInheritanceUtils; |
| import com.intellij.refactoring.psi.TypeParametersVisitor; |
| import com.intellij.refactoring.util.FixableUsageInfo; |
| import com.intellij.refactoring.util.FixableUsagesRefactoringProcessor; |
| import com.intellij.refactoring.util.RefactoringUIUtil; |
| import com.intellij.refactoring.util.RefactoringUtil; |
| import com.intellij.refactoring.util.classMembers.MemberInfo; |
| import com.intellij.usageView.UsageInfo; |
| import com.intellij.usageView.UsageViewDescriptor; |
| import com.intellij.usageView.UsageViewUtil; |
| import com.intellij.util.IncorrectOperationException; |
| import com.intellij.util.VisibilityUtil; |
| import com.intellij.util.containers.ContainerUtil; |
| import com.intellij.util.containers.MultiMap; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| |
| import java.util.*; |
| |
| public class ExtractClassProcessor extends FixableUsagesRefactoringProcessor { |
| private static final Logger logger = Logger.getInstance("com.siyeh.rpp.extractclass.ExtractClassProcessor"); |
| |
| private final PsiClass sourceClass; |
| private final List<PsiField> fields; |
| private final List<PsiMethod> methods; |
| private final List<PsiClass> innerClasses; |
| private final Set<PsiClass> innerClassesToMakePublic = new HashSet<PsiClass>(); |
| private final List<PsiTypeParameter> typeParams = new ArrayList<PsiTypeParameter>(); |
| private final String newPackageName; |
| private final MoveDestination myMoveDestination; |
| private final String myNewVisibility; |
| private final boolean myGenerateAccessors; |
| private final List<PsiField> enumConstants; |
| private final String newClassName; |
| private final String delegateFieldName; |
| private final boolean requiresBackpointer; |
| private boolean delegationRequired = false; |
| private final ExtractEnumProcessor myExtractEnumProcessor; |
| private final PsiClass myClass; |
| |
| public ExtractClassProcessor(PsiClass sourceClass, |
| List<PsiField> fields, |
| List<PsiMethod> methods, |
| List<PsiClass> innerClasses, |
| String newPackageName, |
| String newClassName) { |
| this(sourceClass, fields, methods, innerClasses, newPackageName, null, newClassName, null, false, Collections.<MemberInfo>emptyList()); |
| } |
| |
| public ExtractClassProcessor(PsiClass sourceClass, |
| List<PsiField> fields, |
| List<PsiMethod> methods, |
| List<PsiClass> classes, |
| String packageName, |
| MoveDestination moveDestination, |
| String newClassName, |
| String newVisibility, |
| boolean generateAccessors, List<MemberInfo> enumConstants) { |
| super(sourceClass.getProject()); |
| this.sourceClass = sourceClass; |
| this.newPackageName = packageName; |
| myMoveDestination = moveDestination; |
| myNewVisibility = newVisibility; |
| myGenerateAccessors = generateAccessors; |
| this.enumConstants = new ArrayList<PsiField>(); |
| for (MemberInfo constant : enumConstants) { |
| if (constant.isChecked()) { |
| this.enumConstants.add((PsiField)constant.getMember()); |
| } |
| } |
| this.fields = new ArrayList<PsiField>(fields); |
| this.methods = new ArrayList<PsiMethod>(methods); |
| this.innerClasses = new ArrayList<PsiClass>(classes); |
| this.newClassName = newClassName; |
| delegateFieldName = calculateDelegateFieldName(); |
| requiresBackpointer = new BackpointerUsageVisitor(fields, innerClasses, methods, sourceClass).backpointerRequired(); |
| if (requiresBackpointer) { |
| ContainerUtil.addAll(typeParams, sourceClass.getTypeParameters()); |
| } |
| else { |
| final Set<PsiTypeParameter> typeParamSet = new HashSet<PsiTypeParameter>(); |
| final TypeParametersVisitor visitor = new TypeParametersVisitor(typeParamSet); |
| for (PsiField field : fields) { |
| field.accept(visitor); |
| } |
| for (PsiMethod method : methods) { |
| method.accept(visitor); |
| //do not include method's type parameters in class signature |
| typeParamSet.removeAll(Arrays.asList(method.getTypeParameters())); |
| } |
| typeParams.addAll(typeParamSet); |
| } |
| myClass = new WriteCommandAction<PsiClass>(myProject, getCommandName()){ |
| @Override |
| protected void run(Result<PsiClass> result) throws Throwable { |
| result.setResult(buildClass()); |
| } |
| }.execute().getResultObject(); |
| myExtractEnumProcessor = new ExtractEnumProcessor(myProject, this.enumConstants, myClass); |
| } |
| |
| public PsiClass getCreatedClass() { |
| return myClass; |
| } |
| |
| @Override |
| protected boolean preprocessUsages(final Ref<UsageInfo[]> refUsages) { |
| final MultiMap<PsiElement, String> conflicts = new MultiMap<PsiElement, String>(); |
| myExtractEnumProcessor.findEnumConstantConflicts(refUsages); |
| if (!DestinationFolderComboBox.isAccessible(myProject, sourceClass.getContainingFile().getVirtualFile(), |
| myClass.getContainingFile().getContainingDirectory().getVirtualFile())) { |
| conflicts.putValue(sourceClass, "Extracted class won't be accessible in " + RefactoringUIUtil.getDescription(sourceClass, true)); |
| } |
| ApplicationManager.getApplication().runWriteAction(new Runnable() { |
| public void run() { |
| myClass.delete(); |
| } |
| }); |
| final Project project = sourceClass.getProject(); |
| final GlobalSearchScope scope = GlobalSearchScope.allScope(project); |
| final PsiClass existingClass = |
| JavaPsiFacade.getInstance(project).findClass(StringUtil.getQualifiedName(newPackageName, newClassName), scope); |
| if (existingClass != null) { |
| conflicts.putValue(existingClass, RefactorJBundle.message("cannot.perform.the.refactoring") + |
| RefactorJBundle.message("there.already.exists.a.class.with.the.chosen.name")); |
| } |
| |
| if (!myGenerateAccessors) { |
| calculateInitializersConflicts(conflicts); |
| final NecessaryAccessorsVisitor visitor = checkNecessaryGettersSetters4ExtractedClass(); |
| final NecessaryAccessorsVisitor srcVisitor = checkNecessaryGettersSetters4SourceClass(); |
| final Set<PsiField> fieldsNeedingGetter = new LinkedHashSet<PsiField>(); |
| fieldsNeedingGetter.addAll(visitor.getFieldsNeedingGetter()); |
| fieldsNeedingGetter.addAll(srcVisitor.getFieldsNeedingGetter()); |
| for (PsiField field : fieldsNeedingGetter) { |
| conflicts.putValue(field, "Field \'" + field.getName() + "\' needs getter"); |
| } |
| final Set<PsiField> fieldsNeedingSetter = new LinkedHashSet<PsiField>(); |
| fieldsNeedingSetter.addAll(visitor.getFieldsNeedingSetter()); |
| fieldsNeedingSetter.addAll(srcVisitor.getFieldsNeedingSetter()); |
| for (PsiField field : fieldsNeedingSetter) { |
| conflicts.putValue(field, "Field \'" + field.getName() + "\' needs setter"); |
| } |
| } |
| checkConflicts(refUsages, conflicts); |
| return showConflicts(conflicts, refUsages.get()); |
| } |
| |
| |
| private void calculateInitializersConflicts(MultiMap<PsiElement, String> conflicts) { |
| final PsiClassInitializer[] initializers = sourceClass.getInitializers(); |
| for (PsiClassInitializer initializer : initializers) { |
| if (initializerDependsOnMoved(initializer)) { |
| conflicts.putValue(initializer, "Class initializer requires moved members"); |
| } |
| } |
| for (PsiMethod constructor : sourceClass.getConstructors()) { |
| if (initializerDependsOnMoved(constructor.getBody())) { |
| conflicts.putValue(constructor, "Constructor requires moved members"); |
| } |
| } |
| } |
| |
| private boolean initializerDependsOnMoved(PsiElement initializer) { |
| final boolean [] dependsOnMoved = new boolean[]{false}; |
| initializer.accept(new JavaRecursiveElementWalkingVisitor(){ |
| public void visitReferenceExpression(final PsiReferenceExpression expression) { |
| super.visitReferenceExpression(expression); |
| final PsiElement resolved = expression.resolve(); |
| if (resolved instanceof PsiMember) { |
| dependsOnMoved[0] |= !((PsiMember)resolved).hasModifierProperty(PsiModifier.STATIC) && isInMovedElement(resolved); |
| } |
| } |
| }); |
| return dependsOnMoved[0]; |
| } |
| |
| private String calculateDelegateFieldName() { |
| final Project project = sourceClass.getProject(); |
| final CodeStyleSettingsManager settingsManager = CodeStyleSettingsManager.getInstance(project); |
| final CodeStyleSettings settings = settingsManager.getCurrentSettings(); |
| |
| final String baseName = settings.FIELD_NAME_PREFIX.length() == 0 ? StringUtil.decapitalize(newClassName) : newClassName; |
| String name = settings.FIELD_NAME_PREFIX + baseName + settings.FIELD_NAME_SUFFIX; |
| if (!existsFieldWithName(name) && !PsiNameHelper.getInstance(project).isKeyword(name)) { |
| return name; |
| } |
| int counter = 1; |
| while (true) { |
| name = settings.FIELD_NAME_PREFIX + baseName + counter + settings.FIELD_NAME_SUFFIX; |
| if (!existsFieldWithName(name) && !PsiNameHelper.getInstance(project).isKeyword(name)) { |
| return name; |
| } |
| counter++; |
| } |
| } |
| |
| |
| private boolean existsFieldWithName(String name) { |
| final PsiField[] allFields = sourceClass.getAllFields(); |
| for (PsiField field : allFields) { |
| if (name.equals(field.getName()) && !fields.contains(field)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| protected String getCommandName() { |
| return RefactorJBundle.message("extracted.class.command.name", newClassName); |
| } |
| |
| @NotNull |
| protected UsageViewDescriptor createUsageViewDescriptor(UsageInfo[] usageInfos) { |
| return new ExtractClassUsageViewDescriptor(sourceClass); |
| } |
| |
| protected void performRefactoring(UsageInfo[] usageInfos) { |
| final PsiClass psiClass = buildClass(); |
| if (psiClass == null) return; |
| if (delegationRequired) { |
| buildDelegate(); |
| } |
| myExtractEnumProcessor.performEnumConstantTypeMigration(usageInfos); |
| final Set<PsiMember> members = new HashSet<PsiMember>(); |
| for (PsiMethod method : methods) { |
| final PsiMethod member = psiClass.findMethodBySignature(method, false); |
| if (member != null) { |
| members.add(member); |
| } |
| } |
| for (PsiField field : fields) { |
| final PsiField member = psiClass.findFieldByName(field.getName(), false); |
| if (member != null) { |
| members.add(member); |
| final PsiExpression initializer = member.getInitializer(); |
| if (initializer != null) { |
| final boolean[] moveInitializerToConstructor = new boolean[1]; |
| initializer.accept(new JavaRecursiveElementWalkingVisitor(){ |
| @Override |
| public void visitReferenceExpression(PsiReferenceExpression expression) { |
| super.visitReferenceExpression(expression); |
| final PsiElement resolved = expression.resolve(); |
| if (resolved instanceof PsiField && !members.contains(resolved)) { |
| moveInitializerToConstructor[0] = true; |
| } |
| } |
| }); |
| |
| if (moveInitializerToConstructor[0]) { |
| final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(myProject); |
| PsiMethod[] constructors = psiClass.getConstructors(); |
| if (constructors.length == 0) { |
| final PsiMethod constructor = (PsiMethod)elementFactory.createConstructor().setName(psiClass.getName()); |
| constructors = new PsiMethod[] {(PsiMethod)psiClass.add(constructor)}; |
| } |
| for (PsiMethod constructor : constructors) { |
| MoveInstanceMembersUtil.moveInitializerToConstructor(elementFactory, constructor, member); |
| } |
| } |
| } |
| } |
| } |
| |
| if (myGenerateAccessors) { |
| final NecessaryAccessorsVisitor visitor = checkNecessaryGettersSetters4SourceClass(); |
| for (PsiField field : visitor.getFieldsNeedingGetter()) { |
| sourceClass.add(GenerateMembersUtil.generateGetterPrototype(field)); |
| } |
| |
| for (PsiField field : visitor.getFieldsNeedingSetter()) { |
| sourceClass.add(GenerateMembersUtil.generateSetterPrototype(field)); |
| } |
| } |
| super.performRefactoring(usageInfos); |
| if (myNewVisibility == null) return; |
| for (PsiMember member : members) { |
| VisibilityUtil.fixVisibility(UsageViewUtil.toElements(usageInfos), member, myNewVisibility); |
| } |
| } |
| |
| private NecessaryAccessorsVisitor checkNecessaryGettersSetters4SourceClass() { |
| final NecessaryAccessorsVisitor visitor = new NecessaryAccessorsVisitor() { |
| @Override |
| protected boolean hasGetterOrSetter(PsiMethod[] getters) { |
| for (PsiMethod getter : getters) { |
| if (!isInMovedElement(getter)) return true; |
| } |
| return false; |
| } |
| |
| @Override |
| protected boolean isProhibitedReference(PsiField field) { |
| if (fields.contains(field)) { |
| return false; |
| } |
| if (innerClasses.contains(field.getContainingClass())) { |
| return false; |
| } |
| return true; |
| } |
| }; |
| for (PsiField field : fields) { |
| field.accept(visitor); |
| } |
| for (PsiMethod method : methods) { |
| method.accept(visitor); |
| } |
| for (PsiClass innerClass : innerClasses) { |
| innerClass.accept(visitor); |
| } |
| return visitor; |
| } |
| |
| private NecessaryAccessorsVisitor checkNecessaryGettersSetters4ExtractedClass() { |
| final NecessaryAccessorsVisitor visitor = new NecessaryAccessorsVisitor() { |
| @Override |
| protected boolean hasGetterOrSetter(PsiMethod[] getters) { |
| for (PsiMethod getter : getters) { |
| if (isInMovedElement(getter)) return true; |
| } |
| return false; |
| } |
| |
| @Override |
| protected boolean isProhibitedReference(PsiField field) { |
| if (fields.contains(field)) { |
| return true; |
| } |
| if (innerClasses.contains(field.getContainingClass())) { |
| return true; |
| } |
| return false; |
| } |
| |
| @Override |
| public void visitMethod(PsiMethod method) { |
| if (methods.contains(method)) return; |
| super.visitMethod(method); |
| } |
| |
| @Override |
| public void visitField(PsiField field) { |
| if (fields.contains(field)) return; |
| super.visitField(field); |
| } |
| |
| @Override |
| public void visitClass(PsiClass aClass) { |
| if (innerClasses.contains(aClass)) return; |
| super.visitClass(aClass); |
| } |
| |
| }; |
| sourceClass.accept(visitor); |
| return visitor; |
| } |
| |
| |
| private void buildDelegate() { |
| final PsiManager manager = sourceClass.getManager(); |
| final PsiElementFactory factory = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory(); |
| final CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(manager.getProject()); |
| @NonNls final StringBuilder fieldBuffer = new StringBuilder(); |
| final String delegateVisibility = calculateDelegateVisibility(); |
| if (delegateVisibility.length() > 0) fieldBuffer.append(delegateVisibility).append(' '); |
| fieldBuffer.append("final "); |
| final String fullyQualifiedName = StringUtil.getQualifiedName(newPackageName, newClassName); |
| fieldBuffer.append(fullyQualifiedName); |
| if (!typeParams.isEmpty()) { |
| fieldBuffer.append('<'); |
| for (PsiTypeParameter typeParameter : typeParams) { |
| fieldBuffer.append(typeParameter.getName()); |
| } |
| fieldBuffer.append('>'); |
| } |
| fieldBuffer.append(' '); |
| fieldBuffer.append(delegateFieldName); |
| fieldBuffer.append(" = new ").append(fullyQualifiedName); |
| if (!typeParams.isEmpty()) { |
| fieldBuffer.append('<'); |
| for (PsiTypeParameter typeParameter : typeParams) { |
| fieldBuffer.append(typeParameter.getName()); |
| } |
| fieldBuffer.append('>'); |
| } |
| fieldBuffer.append('('); |
| if (requiresBackpointer) { |
| fieldBuffer.append("this"); |
| } |
| |
| fieldBuffer.append(");"); |
| try { |
| final String fieldString = fieldBuffer.toString(); |
| final PsiField field = factory.createFieldFromText(fieldString, sourceClass); |
| final PsiElement newField = sourceClass.add(field); |
| codeStyleManager.reformat(JavaCodeStyleManager.getInstance(myProject).shortenClassReferences(newField)); |
| } |
| catch (IncorrectOperationException e) { |
| logger.error(e); |
| } |
| } |
| |
| @NonNls |
| private String calculateDelegateVisibility() { |
| for (PsiField field : fields) { |
| if (field.hasModifierProperty(PsiModifier.PUBLIC) && !field.hasModifierProperty(PsiModifier.STATIC)) { |
| return "public"; |
| } |
| } |
| for (PsiField field : fields) { |
| if (field.hasModifierProperty(PsiModifier.PROTECTED) && !field.hasModifierProperty(PsiModifier.STATIC)) { |
| return "protected"; |
| } |
| } |
| for (PsiField field : fields) { |
| if (field.hasModifierProperty(PsiModifier.PACKAGE_LOCAL) && !field.hasModifierProperty(PsiModifier.STATIC)) { |
| return ""; |
| } |
| } |
| return "private"; |
| } |
| |
| public void findUsages(@NotNull List<FixableUsageInfo> usages) { |
| for (PsiField field : fields) { |
| findUsagesForField(field, usages); |
| usages.add(new RemoveField(field)); |
| } |
| usages.addAll(myExtractEnumProcessor.findEnumConstantUsages(new ArrayList<FixableUsageInfo>(usages))); |
| for (PsiClass innerClass : innerClasses) { |
| findUsagesForInnerClass(innerClass, usages); |
| usages.add(new RemoveInnerClass(innerClass)); |
| } |
| for (PsiMethod method : methods) { |
| if (method.hasModifierProperty(PsiModifier.STATIC)) { |
| findUsagesForStaticMethod(method, usages); |
| } |
| else { |
| findUsagesForMethod(method, usages); |
| } |
| } |
| } |
| |
| private void findUsagesForInnerClass(PsiClass innerClass, List<FixableUsageInfo> usages) { |
| final PsiManager psiManager = innerClass.getManager(); |
| final Project project = psiManager.getProject(); |
| final GlobalSearchScope scope = GlobalSearchScope.allScope(project); |
| final Iterable<PsiReference> calls = ReferencesSearch.search(innerClass, scope); |
| final String innerName = innerClass.getQualifiedName(); |
| assert innerName != null; |
| final String sourceClassQualifiedName = sourceClass.getQualifiedName(); |
| assert sourceClassQualifiedName != null; |
| final String newInnerClassName = StringUtil.getQualifiedName(newPackageName, newClassName) + innerName.substring(sourceClassQualifiedName.length()); |
| boolean hasExternalReference = false; |
| for (PsiReference reference : calls) { |
| final PsiElement referenceElement = reference.getElement(); |
| if (referenceElement instanceof PsiJavaCodeReferenceElement) { |
| if (!isInMovedElement(referenceElement)) { |
| usages.add(new ReplaceClassReference((PsiJavaCodeReferenceElement)referenceElement, newInnerClassName)); |
| hasExternalReference = true; |
| } |
| } |
| } |
| if (hasExternalReference) { |
| innerClassesToMakePublic.add(innerClass); |
| } |
| } |
| |
| private void findUsagesForMethod(PsiMethod method, List<FixableUsageInfo> usages) { |
| final PsiManager psiManager = method.getManager(); |
| final Project project = psiManager.getProject(); |
| final GlobalSearchScope scope = GlobalSearchScope.allScope(project); |
| final Iterable<PsiReference> calls = ReferencesSearch.search(method, scope); |
| for (PsiReference reference : calls) { |
| final PsiElement referenceElement = reference.getElement(); |
| final PsiElement parent = referenceElement.getParent(); |
| if (parent instanceof PsiMethodCallExpression) { |
| final PsiMethodCallExpression call = (PsiMethodCallExpression)parent; |
| if (isInMovedElement(call)) { |
| continue; |
| } |
| final PsiReferenceExpression methodExpression = call.getMethodExpression(); |
| final PsiExpression qualifier = methodExpression.getQualifierExpression(); |
| if (qualifier == null || qualifier instanceof PsiThisExpression) { |
| usages.add(new ReplaceThisCallWithDelegateCall(call, delegateFieldName)); |
| } |
| delegationRequired = true; |
| } |
| } |
| |
| if (!delegationRequired && MethodInheritanceUtils.hasSiblingMethods(method)) { |
| delegationRequired = true; |
| } |
| |
| if (delegationRequired) { |
| usages.add(new MakeMethodDelegate(method, delegateFieldName)); |
| } |
| else { |
| usages.add(new RemoveMethod(method)); |
| } |
| } |
| |
| private void findUsagesForStaticMethod(PsiMethod method, List<FixableUsageInfo> usages) { |
| final PsiManager psiManager = method.getManager(); |
| final Project project = psiManager.getProject(); |
| final GlobalSearchScope scope = GlobalSearchScope.allScope(project); |
| final Iterable<PsiReference> calls = ReferencesSearch.search(method, scope); |
| final String fullyQualifiedName = StringUtil.getQualifiedName(newPackageName, newClassName); |
| for (PsiReference reference : calls) { |
| final PsiElement referenceElement = reference.getElement(); |
| |
| final PsiElement parent = referenceElement.getParent(); |
| if (parent instanceof PsiMethodCallExpression) { |
| final PsiMethodCallExpression call = (PsiMethodCallExpression)parent; |
| if (!isInMovedElement(call)) { |
| usages.add(new RetargetStaticMethodCall(call, fullyQualifiedName)); |
| } |
| } else if (parent instanceof PsiImportStaticStatement) { |
| final PsiJavaCodeReferenceElement importReference = ((PsiImportStaticStatement)parent).getImportReference(); |
| if (importReference != null) { |
| final PsiElement qualifier = importReference.getQualifier(); |
| if (qualifier instanceof PsiJavaCodeReferenceElement) { |
| usages.add(new ReplaceClassReference((PsiJavaCodeReferenceElement)qualifier, fullyQualifiedName)); |
| } |
| } |
| } |
| } |
| usages.add(new RemoveMethod(method)); |
| } |
| |
| private boolean isInMovedElement(PsiElement exp) { |
| for (PsiField field : fields) { |
| if (PsiTreeUtil.isAncestor(field, exp, false)) { |
| return true; |
| } |
| } |
| for (PsiMethod method : methods) { |
| if (PsiTreeUtil.isAncestor(method, exp, false)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| private void findUsagesForField(PsiField field, List<FixableUsageInfo> usages) { |
| final PsiManager psiManager = field.getManager(); |
| final Project project = psiManager.getProject(); |
| final GlobalSearchScope scope = GlobalSearchScope.allScope(project); |
| |
| final String qualifiedName = StringUtil.getQualifiedName(newPackageName, newClassName); |
| @NonNls String getter = null; |
| if (myGenerateAccessors) { |
| getter = PropertyUtil.suggestGetterName(field); |
| } else { |
| final PsiMethod fieldGetter = PropertyUtil.findPropertyGetter(sourceClass, field.getName(), false, false); |
| if (fieldGetter != null && isInMovedElement(fieldGetter)) { |
| getter = fieldGetter.getName(); |
| } |
| } |
| |
| @NonNls String setter = null; |
| if (myGenerateAccessors) { |
| setter = PropertyUtil.suggestSetterName(field); |
| } else { |
| final PsiMethod fieldSetter = PropertyUtil.findPropertySetter(sourceClass, field.getName(), false, false); |
| if (fieldSetter != null && isInMovedElement(fieldSetter)) { |
| setter = fieldSetter.getName(); |
| } |
| } |
| final boolean isStatic = field.hasModifierProperty(PsiModifier.STATIC); |
| |
| for (PsiReference reference : ReferencesSearch.search(field, scope)) { |
| final PsiElement element = reference.getElement(); |
| if (isInMovedElement(element)) { |
| continue; |
| } |
| |
| if (element instanceof PsiReferenceExpression) { |
| final PsiReferenceExpression exp = (PsiReferenceExpression)element; |
| if (RefactoringUtil.isPlusPlusOrMinusMinus(exp.getParent())) { |
| usages.add(isStatic |
| ? new ReplaceStaticVariableIncrementDecrement(exp, qualifiedName) |
| : new ReplaceInstanceVariableIncrementDecrement(exp, delegateFieldName, setter, getter, field.getName())); |
| } |
| else if (RefactoringUtil.isAssignmentLHS(exp)) { |
| usages.add(isStatic |
| ? new ReplaceStaticVariableAssignment(exp, qualifiedName) |
| : new ReplaceInstanceVariableAssignment(PsiTreeUtil.getParentOfType(exp, PsiAssignmentExpression.class), |
| delegateFieldName, setter, getter, field.getName())); |
| |
| } |
| else { |
| usages.add(isStatic |
| ? new ReplaceStaticVariableAccess(exp, qualifiedName, enumConstants.contains(field)) |
| : new ReplaceInstanceVariableAccess(exp, delegateFieldName, getter, field.getName())); |
| } |
| |
| if (!isStatic) { |
| delegationRequired = true; |
| } |
| } else if (element instanceof PsiDocTagValue) { |
| usages.add(new BindJavadocReference(element, qualifiedName, field.getName())); |
| } |
| } |
| } |
| |
| |
| private PsiClass buildClass() { |
| final PsiManager manager = sourceClass.getManager(); |
| final Project project = sourceClass.getProject(); |
| final ExtractedClassBuilder extractedClassBuilder = new ExtractedClassBuilder(); |
| extractedClassBuilder.setProject(myProject); |
| extractedClassBuilder.setClassName(newClassName); |
| extractedClassBuilder.setPackageName(newPackageName); |
| extractedClassBuilder.setOriginalClassName(sourceClass.getQualifiedName()); |
| extractedClassBuilder.setRequiresBackPointer(requiresBackpointer); |
| extractedClassBuilder.setExtractAsEnum(enumConstants); |
| for (PsiField field : fields) { |
| extractedClassBuilder.addField(field); |
| } |
| for (PsiMethod method : methods) { |
| extractedClassBuilder.addMethod(method); |
| } |
| for (PsiClass innerClass : innerClasses) { |
| extractedClassBuilder.addInnerClass(innerClass, innerClassesToMakePublic.contains(innerClass)); |
| } |
| extractedClassBuilder.setTypeArguments(typeParams); |
| final List<PsiClass> interfaces = calculateInterfacesSupported(); |
| extractedClassBuilder.setInterfaces(interfaces); |
| |
| if (myGenerateAccessors) { |
| final NecessaryAccessorsVisitor visitor = checkNecessaryGettersSetters4ExtractedClass(); |
| sourceClass.accept(visitor); |
| extractedClassBuilder.setFieldsNeedingGetters(visitor.getFieldsNeedingGetter()); |
| extractedClassBuilder.setFieldsNeedingSetters(visitor.getFieldsNeedingSetter()); |
| } |
| |
| final String classString = extractedClassBuilder.buildBeanClass(); |
| |
| try { |
| final PsiFile containingFile = sourceClass.getContainingFile(); |
| final PsiDirectory directory; |
| final PsiDirectory containingDirectory = containingFile.getContainingDirectory(); |
| if (myMoveDestination != null) { |
| directory = myMoveDestination.getTargetDirectory(containingDirectory); |
| } else { |
| final Module module = ModuleUtil.findModuleForPsiElement(containingFile); |
| assert module != null; |
| directory = PackageUtil.findOrCreateDirectoryForPackage(module, newPackageName, containingDirectory, false, true); |
| } |
| if (directory != null) { |
| final PsiFileFactory factory = PsiFileFactory.getInstance(project); |
| final PsiFile newFile = factory.createFileFromText(newClassName + ".java", JavaFileType.INSTANCE, classString); |
| final PsiElement addedFile = directory.add(newFile); |
| final CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(manager.getProject()); |
| final PsiElement shortenedFile = JavaCodeStyleManager.getInstance(project).shortenClassReferences(addedFile); |
| return ((PsiJavaFile)codeStyleManager.reformat(shortenedFile)).getClasses()[0]; |
| } else { |
| return null; |
| } |
| } |
| catch (IncorrectOperationException e) { |
| return null; |
| } |
| } |
| |
| private List<PsiClass> calculateInterfacesSupported() { |
| final List<PsiClass> out = new ArrayList<PsiClass>(); |
| final PsiClass[] supers = sourceClass.getSupers(); |
| for (PsiClass superClass : supers) { |
| if (!superClass.isInterface()) { |
| continue; |
| } |
| final PsiMethod[] superclassMethods = superClass.getMethods(); |
| if (superclassMethods.length == 0) { |
| continue; |
| } |
| boolean allMethodsCovered = true; |
| |
| for (PsiMethod method : superclassMethods) { |
| boolean isCovered = false; |
| for (PsiMethod movedMethod : methods) { |
| if (isSuperMethod(method, movedMethod)) { |
| isCovered = true; |
| break; |
| } |
| } |
| if (!isCovered) { |
| allMethodsCovered = false; |
| break; |
| } |
| } |
| if (allMethodsCovered) { |
| out.add(superClass); |
| } |
| } |
| final Project project = sourceClass.getProject(); |
| final PsiManager manager = sourceClass.getManager(); |
| final GlobalSearchScope scope = GlobalSearchScope.allScope(project); |
| if (usesDefaultSerialization(sourceClass)) { |
| final PsiClass serializable = JavaPsiFacade.getInstance(manager.getProject()).findClass("java.io.Serializable", scope); |
| out.add(serializable); |
| } |
| if (usesDefaultClone(sourceClass)) { |
| final PsiClass cloneable = JavaPsiFacade.getInstance(manager.getProject()).findClass("java.lang.Cloneable", scope); |
| out.add(cloneable); |
| } |
| return out; |
| } |
| |
| private static boolean isSuperMethod(PsiMethod method, PsiMethod movedMethod) { |
| final PsiMethod[] superMethods = movedMethod.findSuperMethods(); |
| for (PsiMethod testMethod : superMethods) { |
| if (testMethod.equals(method)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| private static boolean usesDefaultClone(PsiClass aClass) { |
| final Project project = aClass.getProject(); |
| final PsiManager manager = aClass.getManager(); |
| final GlobalSearchScope scope = GlobalSearchScope.allScope(project); |
| final PsiClass cloneable = JavaPsiFacade.getInstance(manager.getProject()).findClass("java.lang.Cloneable", scope); |
| if (!InheritanceUtil.isInheritorOrSelf(aClass, cloneable, true)) { |
| return false; |
| } |
| final PsiMethod[] methods = aClass.findMethodsByName("clone", false); |
| for (PsiMethod method : methods) { |
| final PsiParameterList parameterList = method.getParameterList(); |
| final PsiParameter[] parameters = parameterList.getParameters(); |
| if (parameters.length == 0) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| private static boolean usesDefaultSerialization(PsiClass aClass) { |
| final Project project = aClass.getProject(); |
| final PsiManager manager = aClass.getManager(); |
| final GlobalSearchScope scope = GlobalSearchScope.allScope(project); |
| final PsiClass serializable = JavaPsiFacade.getInstance(manager.getProject()).findClass("java.io.Serializable", scope); |
| if (!InheritanceUtil.isInheritorOrSelf(aClass, serializable, true)) { |
| return false; |
| } |
| final PsiMethod[] methods = aClass.findMethodsByName("writeObject", false); |
| for (PsiMethod method : methods) { |
| final PsiParameterList parameterList = method.getParameterList(); |
| final PsiParameter[] parameters = parameterList.getParameters(); |
| if (parameters.length == 1) { |
| final PsiType type = parameters[0].getType(); |
| final String text = type.getCanonicalText(); |
| if ("java.io.DataOutputStream".equals(text)) { |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| |
| private abstract class NecessaryAccessorsVisitor extends JavaRecursiveElementWalkingVisitor { |
| private final Set<PsiField> fieldsNeedingGetter = new HashSet<PsiField>(); |
| private final Set<PsiField> fieldsNeedingSetter = new HashSet<PsiField>(); |
| |
| public void visitReferenceExpression(PsiReferenceExpression expression) { |
| super.visitReferenceExpression(expression); |
| if (isProhibitedReference(expression)) { |
| final PsiField field = getReferencedField(expression); |
| if (!hasGetter(field) && !isStaticFinal(field) && !field.getModifierList().hasModifierProperty(PsiModifier.PUBLIC)) { |
| fieldsNeedingGetter.add(field); |
| } |
| } |
| } |
| |
| private boolean isStaticFinal(PsiField field) { |
| final PsiModifierList modifierList = field.getModifierList(); |
| logger.assertTrue(modifierList != null); |
| return modifierList.hasModifierProperty(PsiModifier.STATIC) && modifierList.hasModifierProperty(PsiModifier.FINAL); |
| } |
| |
| public void visitAssignmentExpression(PsiAssignmentExpression expression) { |
| super.visitAssignmentExpression(expression); |
| |
| final PsiExpression lhs = expression.getLExpression(); |
| if (isProhibitedReference(lhs)) { |
| final PsiField field = getReferencedField(lhs); |
| if (!hasGetter(field) && !isStaticFinal(field) && !field.getModifierList().hasModifierProperty(PsiModifier.PUBLIC)) { |
| fieldsNeedingSetter.add(field); |
| } |
| } |
| } |
| |
| public void visitPostfixExpression(PsiPostfixExpression expression) { |
| super.visitPostfixExpression(expression); |
| checkSetterNeeded(expression.getOperand(), expression.getOperationSign()); |
| } |
| |
| public void visitPrefixExpression(PsiPrefixExpression expression) { |
| super.visitPrefixExpression(expression); |
| checkSetterNeeded(expression.getOperand(), expression.getOperationSign()); |
| } |
| |
| private void checkSetterNeeded(final PsiExpression operand, final PsiJavaToken sign) { |
| final IElementType tokenType = sign.getTokenType(); |
| if (!tokenType.equals(JavaTokenType.PLUSPLUS) && !tokenType.equals(JavaTokenType.MINUSMINUS)) { |
| return; |
| } |
| if (isProhibitedReference(operand)) { |
| final PsiField field = getReferencedField(operand); |
| if (!hasSetter(field) && !isStaticFinal(field)) { |
| fieldsNeedingSetter.add(field); |
| } |
| } |
| } |
| |
| public Set<PsiField> getFieldsNeedingGetter() { |
| return fieldsNeedingGetter; |
| } |
| |
| public Set<PsiField> getFieldsNeedingSetter() { |
| return fieldsNeedingSetter; |
| } |
| |
| private boolean hasGetter(final PsiField field) { |
| return hasGetterOrSetter(sourceClass.findMethodsBySignature(PropertyUtil.generateGetterPrototype(field), false)); |
| } |
| |
| private boolean hasSetter(final PsiField field) { |
| return hasGetterOrSetter(sourceClass.findMethodsBySignature(PropertyUtil.generateSetterPrototype(field), false)); |
| } |
| |
| protected abstract boolean hasGetterOrSetter(final PsiMethod[] getters); |
| |
| protected boolean isProhibitedReference(PsiExpression expression) { |
| return BackpointerUtil.isBackpointerReference(expression, new Condition<PsiField>() { |
| public boolean value(final PsiField field) { |
| return NecessaryAccessorsVisitor.this.isProhibitedReference(field); |
| } |
| }); |
| } |
| |
| protected abstract boolean isProhibitedReference(PsiField field); |
| |
| private PsiField getReferencedField(PsiExpression expression) { |
| if (expression instanceof PsiParenthesizedExpression) { |
| final PsiExpression contents = ((PsiParenthesizedExpression)expression).getExpression(); |
| return getReferencedField(contents); |
| } |
| final PsiReferenceExpression reference = (PsiReferenceExpression)expression; |
| return (PsiField)reference.resolve(); |
| } |
| } |
| } |