org.eclipse.che.jdt.refactoring.RefactoringManager.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.che.jdt.refactoring.RefactoringManager.java

Source

/*******************************************************************************
 * Copyright (c) 2012-2015 Codenvy, S.A.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *   Codenvy, S.A. - initial API and implementation
 *******************************************************************************/

package org.eclipse.che.jdt.refactoring;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.RemovalListener;
import com.google.common.cache.RemovalNotification;
import com.google.inject.Singleton;

import org.eclipse.che.commons.schedule.ScheduleRate;
import org.eclipse.che.dto.server.DtoFactory;
import org.eclipse.che.ide.ext.java.shared.dto.LinkedModeModel;
import org.eclipse.che.ide.ext.java.shared.dto.refactoring.ChangeCreationResult;
import org.eclipse.che.ide.ext.java.shared.dto.refactoring.ChangeEnabledState;
import org.eclipse.che.ide.ext.java.shared.dto.refactoring.ChangePreview;
import org.eclipse.che.ide.ext.java.shared.dto.refactoring.LinkedRenameRefactoringApply;
import org.eclipse.che.ide.ext.java.shared.dto.refactoring.MoveSettings;
import org.eclipse.che.ide.ext.java.shared.dto.refactoring.RefactoringChange;
import org.eclipse.che.ide.ext.java.shared.dto.refactoring.RefactoringPreview;
import org.eclipse.che.ide.ext.java.shared.dto.refactoring.RefactoringResult;
import org.eclipse.che.ide.ext.java.shared.dto.refactoring.RefactoringStatus;
import org.eclipse.che.ide.ext.java.shared.dto.refactoring.RenameRefactoringSession;
import org.eclipse.che.ide.ext.java.shared.dto.refactoring.RenameSettings;
import org.eclipse.che.ide.ext.java.shared.dto.refactoring.ReorgDestination;
import org.eclipse.che.ide.ext.java.shared.dto.refactoring.ValidateNewName;
import org.eclipse.che.jdt.refactoring.session.MoveRefactoringSession;
import org.eclipse.che.jdt.refactoring.session.RefactoringSession;
import org.eclipse.che.jdt.refactoring.session.RenameLinkedModeRefactoringSession;
import org.eclipse.che.jdt.refactoring.session.RenameSession;
import org.eclipse.che.jdt.refactoring.session.ReorgRefactoringSession;
import org.eclipse.che.jdt.util.JdtFlags;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.AssertionFailedException;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.ILocalVariable;
import org.eclipse.jdt.core.IMember;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.ITypeParameter;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.core.JavaModelManager;
import org.eclipse.jdt.internal.corext.refactoring.reorg.IReorgPolicy;
import org.eclipse.jdt.internal.corext.refactoring.reorg.JavaMoveProcessor;
import org.eclipse.jdt.internal.corext.refactoring.reorg.NullReorgQueries;
import org.eclipse.jdt.internal.corext.refactoring.reorg.ReorgPolicyFactory;
import org.eclipse.jdt.ui.refactoring.RenameSupport;
import org.eclipse.ltk.core.refactoring.Refactoring;
import org.eclipse.ltk.core.refactoring.participants.MoveRefactoring;
import org.eclipse.ltk.core.refactoring.participants.RenameRefactoring;
import org.eclipse.ltk.internal.ui.refactoring.ChangePreviewViewerDescriptor;
import org.eclipse.ltk.internal.ui.refactoring.PreviewNode;
import org.eclipse.ltk.ui.refactoring.IChangePreviewViewer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.InvocationTargetException;
import java.util.Collections;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import static org.eclipse.che.ide.ext.java.shared.dto.refactoring.RenameRefactoringSession.RenameWizard;
import static org.eclipse.che.ide.ext.java.shared.dto.refactoring.ReorgDestination.DestinationType;

/**
 * Manager for all refactoring sessions. Handles creating caching and applying refactorings.
 * @author Evgen Vidolob
 * @author Valeriy Svydenko
 */
@Singleton
public class RefactoringManager {
    private static final Logger LOG = LoggerFactory.getLogger(RefactoringManager.class);
    private static final AtomicInteger sessionId = new AtomicInteger(1);
    private final Cache<String, RefactoringSession> sessions;

    public RefactoringManager() {
        sessions = CacheBuilder.newBuilder().expireAfterAccess(15, TimeUnit.MINUTES)
                .removalListener(new RemovalListener<String, RefactoringSession>() {
                    @Override
                    public void onRemoval(RemovalNotification<String, RefactoringSession> notification) {
                        RefactoringSession value = notification.getValue();
                        if (value != null) {
                            value.dispose();
                        }
                    }
                }).build();
    }

    private static RenameSupport createRenameSupport(IJavaElement element, String newName, int flags)
            throws CoreException {
        switch (element.getElementType()) {
        case IJavaElement.PACKAGE_FRAGMENT:
            return RenameSupport.create((IPackageFragment) element, newName, flags);
        case IJavaElement.COMPILATION_UNIT:
            return RenameSupport.create((ICompilationUnit) element, newName, flags);
        case IJavaElement.TYPE:
            return RenameSupport.create((IType) element, newName, flags);
        case IJavaElement.METHOD:
            final IMethod method = (IMethod) element;
            if (method.isConstructor())
                return createRenameSupport(method.getDeclaringType(), newName, flags);
            else
                return RenameSupport.create((IMethod) element, newName, flags);
        case IJavaElement.FIELD:
            return RenameSupport.create((IField) element, newName, flags);
        case IJavaElement.TYPE_PARAMETER:
            return RenameSupport.create((ITypeParameter) element, newName, flags);
        case IJavaElement.LOCAL_VARIABLE:
            return RenameSupport.create((ILocalVariable) element, newName, flags);
        }
        return null;
    }

    /**
     * Create move refactoring session.
     *
     * @param javaElements the java elements
     * @return the ID of the refactoring session
     */
    public String createMoveRefactoringSession(IJavaElement[] javaElements)
            throws JavaModelException, RefactoringException {
        IReorgPolicy.IMovePolicy policy = ReorgPolicyFactory.createMovePolicy(new IResource[0], javaElements);
        if (policy.canEnable()) {
            JavaMoveProcessor processor = new JavaMoveProcessor(policy);
            //TODO this may overwrite existing sources.
            processor.setReorgQueries(new NullReorgQueries());
            processor.setCreateTargetQueries(() -> null);
            Refactoring refactoring = new MoveRefactoring(processor);
            MoveRefactoringSession session = new MoveRefactoringSession(refactoring, processor);
            final String id = String.format("move-%s", sessionId.getAndIncrement());
            sessions.put(id, session);
            return id;
        } else {
            throw new RefactoringException("Can't create move refactoring session.");
        }
    }

    /**
     * Periodically cleanup cache, to avoid memory leak.
     */
    @ScheduleRate(initialDelay = 1, period = 1, unit = TimeUnit.HOURS)
    void cacheClenup() {
        sessions.cleanUp();
    }

    public RefactoringStatus setRefactoringDestination(ReorgDestination destination)
            throws RefactoringException, JavaModelException {
        RefactoringSession session = getRefactoringSession(destination.getSessionId());
        if (!(session instanceof ReorgRefactoringSession)) {
            throw new RefactoringException("Can't set destination on none reorg refactoring session.");
        }

        ReorgRefactoringSession rs = ((ReorgRefactoringSession) session);
        Object dest = getDestination(destination.getProjectPath(), destination.getType(),
                destination.getDestination());
        org.eclipse.ltk.core.refactoring.RefactoringStatus refactoringStatus = rs.verifyDestination(dest);

        return DtoConverter.toRefactoringStatusDto(refactoringStatus);
    }

    private RefactoringSession getRefactoringSession(String sessionId) throws RefactoringException {
        RefactoringSession session = sessions.getIfPresent(sessionId);
        if (session == null) {
            throw new RefactoringException("Can't find refactoring session.");
        }
        return session;
    }

    private Object getDestination(String projectPath, DestinationType type, String destination)
            throws RefactoringException, JavaModelException {
        IJavaProject javaProject = JavaModelManager.getJavaModelManager().getJavaModel()
                .getJavaProject(projectPath);
        if (javaProject == null) {
            throw new RefactoringException("Can't find project: " + projectPath);
        }
        switch (type) {
        case PACKAGE:
            return javaProject.findPackageFragment(new Path(destination));

        case RESOURCE:
        case SOURCE_REFERENCE:
        default:
            throw new UnsupportedOperationException("Can't use destination for 'RESOURCE' or 'SOURCE_REFERENCE'.");

        }
    }

    /**
     * Sets move refactoring settings.
     * update references, update qualified names, files pattern
     * @param settings the move refactoring settings
     * @throws RefactoringException when move refactoring session not found.
     */
    public void setMoveSettings(MoveSettings settings) throws RefactoringException {
        RefactoringSession session = getRefactoringSession(settings.getSessionId());
        if (!(session instanceof MoveRefactoringSession)) {
            throw new RefactoringException("Can't set move on none move refactoring session.");
        }

        MoveRefactoringSession refactoring = ((MoveRefactoringSession) session);
        refactoring.setUpdateReferences(settings.isUpdateReferences());
        if (settings.isUpdateQualifiedNames()) {
            refactoring.setFilePatterns(settings.getFilePatterns());
        }
        refactoring.setUpdateQualifiedNames(settings.isUpdateQualifiedNames());
    }

    /**
     * Get refactoring preview tree.
     * @param sessionId id of the refactoring session
     * @return refactoring preview
     * @throws RefactoringException  when refactoring session not found.
     */
    public RefactoringPreview getRefactoringPreview(String sessionId) throws RefactoringException {
        RefactoringSession session = getRefactoringSession(sessionId);
        PreviewNode node = session.getChangePreview();
        return DtoConverter.toRefactoringPreview(node);
    }

    /**
     * Create refactoring change and return status of creating changes.
     * @param sessionId id of the refactoring session
     * @return change creations result
     * @throws RefactoringException when refactoring session not found.
     */
    public ChangeCreationResult createChange(String sessionId) throws RefactoringException {
        RefactoringSession session = getRefactoringSession(sessionId);
        return session.createChange();
    }

    /**
     * Apply refactoring.
     * @param sessionId id of the refactoring session
     * @return refactoring result
     * @throws RefactoringException  when refactoring session not found.
     */
    public RefactoringResult applyRefactoring(String sessionId) throws RefactoringException {
        RefactoringSession session = getRefactoringSession(sessionId);
        RefactoringResult result = session.apply();
        deleteRefactoringSession(sessionId);
        return result;
    }

    private void deleteRefactoringSession(String sessionId) {
        sessions.invalidate(sessionId);
    }

    /**
     * Create rename refactoring. It can create two rename refactoring types.
     * First is linked mode rename refactoring, second is classic rename refactoring with wizard.
     * @param element element to rename
     * @param cu compilation unit which element belongs. null if element is IPackageFragment.
     * @param offset cursor position inside editor, used only for linked mode
     * @param lightweight if true try to create linked mode refactoring
     * @return rename refactoring session
     * @throws CoreException when impossible to create RenameSupport
     * @throws RefactoringException when we don't support renaming provided element
     */
    public RenameRefactoringSession createRenameRefactoring(IJavaElement element, ICompilationUnit cu, int offset,
            boolean lightweight) throws CoreException, RefactoringException {

        //package fragments are always renamed with wizard
        RenameRefactoringSession session = DtoFactory.newDto(RenameRefactoringSession.class);
        String id = String.format("rename-%s", sessionId.getAndIncrement());
        session.setSessionId(id);
        session.setOldName(element.getElementName());
        session.setWizardType(getWizardType(element));
        if (lightweight && !(element instanceof IPackageFragment)) {
            RenameLinkedModeRefactoringSession refactoringSession = new RenameLinkedModeRefactoringSession(element,
                    cu, offset);
            LinkedModeModel model = refactoringSession.getModel();
            if (model == null) {
                //we don't find edits for this element, so show wizard
                session.setMastShowWizard(true);
            } else {
                session.setMastShowWizard(false);
                session.setLinkedModeModel(model);
            }
            sessions.put(id, refactoringSession);
            return session;
        } else {
            RenameSupport renameSupport = createRenameSupport(element, null, RenameSupport.UPDATE_REFERENCES);
            if (renameSupport != null && renameSupport.preCheck().isOK()) {
                RenameRefactoring refactoring = renameSupport.getfRefactoring();
                RenameSession renameSession = new RenameSession(refactoring);
                session.setMastShowWizard(true);
                sessions.put(id, renameSession);
                return session;

            }
            throw new RefactoringException(
                    "Can't create refactoring session for element: " + element.getElementName());
        }
    }

    private RenameWizard getWizardType(IJavaElement element) throws JavaModelException {
        switch (element.getElementType()) {
        case IJavaElement.PACKAGE_FRAGMENT:
            return RenameWizard.PACKAGE;
        case IJavaElement.COMPILATION_UNIT:
            return RenameWizard.COMPILATION_UNIT;
        case IJavaElement.TYPE:
            return RenameWizard.TYPE;
        case IJavaElement.METHOD:
            final IMethod method = (IMethod) element;
            if (method.isConstructor())
                return RenameWizard.TYPE;
            else
                return RenameWizard.METHOD;
        case IJavaElement.FIELD:
            if (JdtFlags.isEnum((IMember) element)) {
                return RenameWizard.ENUM_CONSTANT;
            }
            return RenameWizard.FIELD;
        case IJavaElement.TYPE_PARAMETER:
            return RenameWizard.TYPE_PARAMETER;
        case IJavaElement.LOCAL_VARIABLE:
            return RenameWizard.LOCAL_VARIABLE;
        }
        return null;
    }

    /**
     * Apply linked mode rename refactoring.
     * @param apply  contains new element name
     * @return refactoring result
     * @throws RefactoringException  when refactoring session not found.
     * @throws CoreException when impossible to apply rename refactoring
     */
    public RefactoringResult applyLinkedRename(LinkedRenameRefactoringApply apply)
            throws RefactoringException, CoreException {
        RefactoringSession session = getRefactoringSession(apply.getSessionId());
        if (session instanceof RenameLinkedModeRefactoringSession) {
            RenameLinkedModeRefactoringSession renameSession = (RenameLinkedModeRefactoringSession) session;
            try {
                RefactoringResult refactoringResult = renameSession.doRename(apply.getNewName());
                deleteRefactoringSession(apply.getSessionId());
                return refactoringResult;
            } catch (InvocationTargetException | InterruptedException | AssertionFailedException e) {
                LOG.error(e.getMessage(), e);
                return DtoConverter.toRefactoringResultDto(
                        org.eclipse.ltk.core.refactoring.RefactoringStatus.createFatalErrorStatus(e.getMessage()));
            }
        }

        throw new RefactoringException("There is no RenameLinkedModeRefactoringSession.");

    }

    /**
     * Set rename refactoring wizard setting. This settings common for all 8 rename wizards.
     * @param settings refactoring wizard settings
     * @throws RefactoringException when refactoring session not found or corresponding session is not RenameSession.
     */
    public void setRenameSettings(RenameSettings settings) throws RefactoringException {
        RefactoringSession session = getRefactoringSession(settings.getSessionId());
        if (session instanceof RenameSession) {
            ((RenameSession) session).setSettings(settings);
        } else {
            throw new RefactoringException("Rename settings may be applied only to RenameSession");
        }
    }

    /**
     * Validate new name for rename refactoring. Uses from wizard.
     * @param newName the new element name
     * @return validation status
     * @throws RefactoringException when corresponding session is not RenameSession.
     */
    public RefactoringStatus renameValidateNewName(ValidateNewName newName) throws RefactoringException {
        RefactoringSession session = getRefactoringSession(newName.getSessionId());
        if (session instanceof RenameSession) {
            return DtoConverter
                    .toRefactoringStatusDto(((RenameSession) session).validateNewName(newName.getNewName()));
        } else {
            throw new RefactoringException("Validating of new name only available on RenameSession.");
        }
    }

    /**
     * Include/exclude refactoring change from refactoring
     * @param state updating state
     * @throws RefactoringException when refactoring session not found.
     */
    public void changeChangeEnabled(ChangeEnabledState state) throws RefactoringException {
        RefactoringSession session = getRefactoringSession(state.getSessionId());
        session.updateChangeEnabled(state.getChangeId(), state.isEnabled());
    }

    /**
     * generate preview for refactoring change
     * @param change the refactoring change
     * @return refactoring change preview
     * @throws RefactoringException when refactoring session or change not found.
     */
    public ChangePreview getChangePreview(RefactoringChange change) throws RefactoringException {
        RefactoringSession session = getRefactoringSession(change.getSessionId());
        PreviewNode previewNode = session.getChangePreview(change.getChangeId());
        try {
            ChangePreviewViewerDescriptor descriptor = previewNode.getChangePreviewViewerDescriptor();
            if (descriptor != null) {
                IChangePreviewViewer viewer = descriptor.createViewer();
                if (viewer != null) {
                    return previewNode.feedInput(viewer, Collections.EMPTY_LIST);
                }
            }
        } catch (CoreException e) {
            throw new RefactoringException(e.getMessage());
        }
        return null;
    }

    /**
     * Make reindex for the project.
     *
     * @param javaProject
     *         java project
     * @throws JavaModelException
     *         when something is wrong
     */
    public void reindexProject(IJavaProject javaProject) throws JavaModelException {
        if (javaProject != null) {
            JavaModelManager.getIndexManager().indexAll(javaProject.getProject());
        }
    }
}