com.intellij.plugins.haxe.ide.refactoring.extractSuperclass.ExtractSuperClassUtil.java Source code

Java tutorial

Introduction

Here is the source code for com.intellij.plugins.haxe.ide.refactoring.extractSuperclass.ExtractSuperClassUtil.java

Source

/*
 * 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.plugins.haxe.ide.refactoring.extractSuperclass;

import com.intellij.codeInsight.generation.OverrideImplementExploreUtil;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.roots.impl.DirectoryIndex;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.plugins.haxe.ide.HaxeFileTemplateUtil;
import com.intellij.plugins.haxe.ide.refactoring.memberPullUp.PullUpProcessor;
import com.intellij.plugins.haxe.lang.psi.HaxeFile;
import com.intellij.plugins.haxe.lang.psi.HaxeInheritList;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.util.*;
import com.intellij.refactoring.listeners.RefactoringEventData;
import com.intellij.refactoring.listeners.RefactoringEventListener;
import com.intellij.refactoring.ui.ConflictsDialog;
import com.intellij.refactoring.util.DocCommentPolicy;
import com.intellij.refactoring.util.RefactoringUtil;
import com.intellij.refactoring.util.classMembers.MemberInfo;
import com.intellij.util.Function;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.HashMap;
import com.intellij.util.containers.MultiMap;
import com.intellij.webcore.ModuleHelper;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.Nullable;

import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * @author dsl
 */
public class ExtractSuperClassUtil {
    private static final Logger LOG = Logger
            .getInstance("com.intellij.refactoring.extractSuperclass.ExtractSuperClassUtil");
    public static final String REFACTORING_EXTRACT_SUPER_ID = "refactoring.extractSuper";

    private ExtractSuperClassUtil() {
    }

    public static PsiClass extractSuperClass(final Project project, final PsiDirectory targetDirectory,
            final String superclassName, final PsiClass subclass, final MemberInfo[] selectedMemberInfos,
            final DocCommentPolicy javaDocPolicy) throws IncorrectOperationException {

        project.getMessageBus().syncPublisher(RefactoringEventListener.REFACTORING_EVENT_TOPIC)
                .refactoringStarted(REFACTORING_EXTRACT_SUPER_ID, createBeforeData(subclass, selectedMemberInfos));

        //final PsiClass superclass = JavaDirectoryService.getInstance().createClass(targetDirectory, superclassName);

        /*for (FileTemplate template : FileTemplateManager.getInstance().getInternalTemplates()) {
          Logger.getInstance(ExtractSuperClassUtil.class).error(template.getName());
        }*/

        Module[] modules = ModuleHelper.getModules(project);
        //PsiClass superclass = CreateClassUtil.createClassFromCustomTemplate(targetDirectory, modules[0], superclassName, "HaxeClass.hx.ft");
        String packageName = DirectoryIndex.getInstance(targetDirectory.getProject())
                .getPackageName(targetDirectory.getVirtualFile());
        PsiClass superclass = null;
        try {
            HaxeFile haxeFile = (HaxeFile) HaxeFileTemplateUtil.createClass(superclassName, packageName,
                    targetDirectory, "HaxeClass", null);
            PsiClass[] classes = haxeFile.getClasses();

            for (PsiClass psiClass : classes) {
                if (psiClass.getName().equals(superclassName)) {
                    superclass = psiClass;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        try {
            final PsiModifierList superClassModifierList = superclass.getModifierList();
            assert superClassModifierList != null;
            //superClassModifierList.setModifierProperty(PsiModifier.FINAL, false);
            //superClassModifierList.setModifierProperty(PsiModifier.PUBLIC, false);
            final PsiReferenceList subClassExtends = subclass.getExtendsList();
            if (subClassExtends != null) {
                copyPsiReferenceList(subClassExtends, superclass.getExtendsList());
            } else if (subclass instanceof PsiAnonymousClass) {
                superclass.getExtendsList().add(((PsiAnonymousClass) subclass).getBaseClassReference());
            }

            // create constructors if neccesary
            PsiMethod[] constructors = getCalledBaseConstructors(subclass);
            if (constructors.length > 0) {
                createConstructorsByPattern(project, superclass, constructors);
            }

            // clear original class' "extends" list
            if (subClassExtends != null) {
                clearPsiReferenceList(subclass.getExtendsList());
            }

            // make original class extend extracted superclass
            //PsiReferenceList extendsList = subclass.getExtendsList();

            if (PsiTreeUtil.getChildOfType(subclass, HaxeInheritList.class) == null) {
                Document document = PsiDocumentManager.getInstance(project)
                        .getDocument(subclass.getContainingFile());
                document.insertString(subclass.getNameIdentifier().getTextRange().getEndOffset(),
                        " extends " + superclassName);
            }

            PsiJavaCodeReferenceElement ref = createExtendingReference(superclass, subclass, selectedMemberInfos);
            if (subClassExtends != null) {
                subclass.getExtendsList().add(ref);
            } else if (subclass instanceof PsiAnonymousClass) {
                ((PsiAnonymousClass) subclass).getBaseClassReference().replace(ref);
            }

            PullUpProcessor pullUpHelper = new PullUpProcessor(subclass, superclass, selectedMemberInfos,
                    javaDocPolicy);

            pullUpHelper.moveMembersToBase();
            pullUpHelper.moveFieldInitializations();

            Collection<MethodSignature> toImplement = OverrideImplementExploreUtil
                    .getMethodSignaturesToImplement(superclass);
            if (!toImplement.isEmpty()) {
                superClassModifierList.setModifierProperty(PsiModifier.ABSTRACT, true);
            }
            return superclass;
        } finally {
            project.getMessageBus().syncPublisher(RefactoringEventListener.REFACTORING_EVENT_TOPIC)
                    .refactoringDone(REFACTORING_EXTRACT_SUPER_ID, createAfterData(superclass));
        }
    }

    private static void createConstructorsByPattern(Project project, final PsiClass superclass,
            PsiMethod[] patternConstructors) throws IncorrectOperationException {
        PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory();
        CodeStyleManager styleManager = CodeStyleManager.getInstance(project);
        for (PsiMethod baseConstructor : patternConstructors) {
            PsiMethod constructor = (PsiMethod) superclass.add(factory.createConstructor());
            PsiParameterList paramList = constructor.getParameterList();
            PsiParameter[] baseParams = baseConstructor.getParameterList().getParameters();
            @NonNls
            StringBuilder superCallText = new StringBuilder();
            superCallText.append("super(");
            final PsiClass baseClass = baseConstructor.getContainingClass();
            LOG.assertTrue(baseClass != null);
            final PsiSubstitutor classSubstitutor = TypeConversionUtil.getSuperClassSubstitutor(baseClass,
                    superclass, PsiSubstitutor.EMPTY);
            for (int i = 0; i < baseParams.length; i++) {
                final PsiParameter baseParam = baseParams[i];
                final PsiParameter newParam = (PsiParameter) paramList.add(factory
                        .createParameter(baseParam.getName(), classSubstitutor.substitute(baseParam.getType())));
                if (i > 0) {
                    superCallText.append(",");
                }
                superCallText.append(newParam.getName());
            }
            superCallText.append(");");
            PsiStatement statement = factory.createStatementFromText(superCallText.toString(), null);
            statement = (PsiStatement) styleManager.reformat(statement);
            final PsiCodeBlock body = constructor.getBody();
            assert body != null;
            body.add(statement);
            constructor.getThrowsList().replace(baseConstructor.getThrowsList());
        }
    }

    private static PsiMethod[] getCalledBaseConstructors(final PsiClass subclass) {
        Set<PsiMethod> baseConstructors = new HashSet<PsiMethod>();
        PsiMethod[] constructors = subclass.getConstructors();
        for (PsiMethod constructor : constructors) {
            PsiCodeBlock body = constructor.getBody();
            if (body == null)
                continue;
            PsiStatement[] statements = body.getStatements();
            if (statements.length > 0) {
                PsiStatement first = statements[0];
                if (first instanceof PsiExpressionStatement) {
                    PsiExpression expression = ((PsiExpressionStatement) first).getExpression();
                    if (expression instanceof PsiMethodCallExpression) {
                        PsiReferenceExpression calledMethod = ((PsiMethodCallExpression) expression)
                                .getMethodExpression();
                        @NonNls
                        String text = calledMethod.getText();
                        if ("super".equals(text)) {
                            PsiMethod baseConstructor = (PsiMethod) calledMethod.resolve();
                            if (baseConstructor != null) {
                                baseConstructors.add(baseConstructor);
                            }
                        }
                    }
                }
            }
        }
        return baseConstructors.toArray(new PsiMethod[baseConstructors.size()]);
    }

    private static void clearPsiReferenceList(PsiReferenceList refList) throws IncorrectOperationException {
        PsiJavaCodeReferenceElement[] refs = refList.getReferenceElements();
        for (PsiJavaCodeReferenceElement ref : refs) {
            ref.delete();
        }
    }

    private static void copyPsiReferenceList(PsiReferenceList sourceList, PsiReferenceList destinationList)
            throws IncorrectOperationException {
        clearPsiReferenceList(destinationList);
        PsiJavaCodeReferenceElement[] refs = sourceList.getReferenceElements();
        for (PsiJavaCodeReferenceElement ref : refs) {
            destinationList.add(ref);
        }
    }

    public static PsiJavaCodeReferenceElement createExtendingReference(final PsiClass superClass,
            final PsiClass derivedClass, final MemberInfo[] selectedMembers) throws IncorrectOperationException {
        final PsiManager manager = derivedClass.getManager();
        Set<PsiElement> movedElements = new com.intellij.util.containers.HashSet<PsiElement>();
        for (final MemberInfo info : selectedMembers) {
            movedElements.add(info.getMember());
        }
        final Condition<PsiTypeParameter> filter = new Condition<PsiTypeParameter>() {
            @Override
            public boolean value(PsiTypeParameter parameter) {
                return findTypeParameterInDerived(derivedClass, parameter.getName()) == parameter;
            }
        };
        final PsiTypeParameterList typeParameterList = RefactoringUtil
                .createTypeParameterListWithUsedTypeParameters(null, filter,
                        PsiUtilCore.toPsiElementArray(movedElements));
        final PsiTypeParameterList originalTypeParameterList = superClass.getTypeParameterList();
        assert originalTypeParameterList != null;
        //final PsiTypeParameterList newList = typeParameterList != null ? (PsiTypeParameterList)originalTypeParameterList.replace(typeParameterList) : originalTypeParameterList;
        final PsiElementFactory factory = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory();
        Map<PsiTypeParameter, PsiType> substitutionMap = new HashMap<PsiTypeParameter, PsiType>();
        //for (final PsiTypeParameter parameter : newList.getTypeParameters()) {
        //  final PsiTypeParameter parameterInDerived = findTypeParameterInDerived(derivedClass, parameter.getName());
        //  if (parameterInDerived != null) {
        //    substitutionMap.put(parameter, factory.createType(parameterInDerived));
        //  }
        //}

        final PsiClassType type = factory.createType(superClass, factory.createSubstitutor(substitutionMap));
        return factory.createReferenceElementByType(type);
    }

    @Nullable
    public static PsiTypeParameter findTypeParameterInDerived(final PsiClass aClass, final String name) {
        for (PsiTypeParameter typeParameter : PsiUtil.typeParametersIterable(aClass)) {
            if (name.equals(typeParameter.getName()))
                return typeParameter;
        }

        return null;
    }

    public static void checkSuperAccessible(PsiDirectory targetDirectory, MultiMap<PsiElement, String> conflicts,
            final PsiClass subclass) {
        final VirtualFile virtualFile = subclass.getContainingFile().getVirtualFile();
        if (virtualFile != null) {
            final boolean inTestSourceContent = ProjectRootManager.getInstance(subclass.getProject()).getFileIndex()
                    .isInTestSourceContent(virtualFile);
            final Module module = ModuleUtil.findModuleForFile(virtualFile, subclass.getProject());
            if (targetDirectory != null && module != null
                    && !GlobalSearchScope.moduleWithDependenciesAndLibrariesScope(module, inTestSourceContent)
                            .contains(targetDirectory.getVirtualFile())) {
                conflicts.putValue(subclass, "Superclass won't be accessible in subclass");
            }
        }
    }

    public static boolean showConflicts(DialogWrapper dialog, MultiMap<PsiElement, String> conflicts,
            final Project project) {
        if (!conflicts.isEmpty()) {
            fireConflictsEvent(conflicts, project);
            ConflictsDialog conflictsDialog = new ConflictsDialog(project, conflicts);
            conflictsDialog.show();
            final boolean ok = conflictsDialog.isOK();
            if (!ok && conflictsDialog.isShowConflicts())
                dialog.close(DialogWrapper.CANCEL_EXIT_CODE);
            return ok;
        }
        return true;
    }

    private static void fireConflictsEvent(MultiMap<PsiElement, String> conflicts, Project project) {
        final RefactoringEventData conflictUsages = new RefactoringEventData();
        conflictUsages.putUserData(RefactoringEventData.CONFLICTS_KEY, conflicts.values());
        project.getMessageBus().syncPublisher(RefactoringEventListener.REFACTORING_EVENT_TOPIC)
                .conflictsDetected(REFACTORING_EXTRACT_SUPER_ID, conflictUsages);
    }

    public static RefactoringEventData createBeforeData(final PsiClass subclassClass, final MemberInfo[] members) {
        RefactoringEventData data = new RefactoringEventData();
        data.addElement(subclassClass);
        data.addMembers(members, new Function<MemberInfo, PsiElement>() {
            @Override
            public PsiElement fun(MemberInfo info) {
                return info.getMember();
            }
        });
        return data;
    }

    public static RefactoringEventData createAfterData(final PsiClass subClass) {
        RefactoringEventData data = new RefactoringEventData();
        data.addElement(subClass);
        return data;
    }
}