org.eclipse.xtext.ui.refactoring.impl.AbstractReferenceUpdater.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.xtext.ui.refactoring.impl.AbstractReferenceUpdater.java

Source

/*******************************************************************************
 * Copyright (c) 2010 itemis AG (http://www.itemis.eu) and others.
 * 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
 *******************************************************************************/
package org.eclipse.xtext.ui.refactoring.impl;

import static com.google.common.collect.Lists.*;
import static com.google.common.collect.Sets.*;
import static org.eclipse.ltk.core.refactoring.RefactoringStatus.*;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.xtext.resource.IReferenceDescription;
import org.eclipse.xtext.ui.refactoring.ElementRenameArguments;
import org.eclipse.xtext.ui.refactoring.IRefactoringUpdateAcceptor;
import org.eclipse.xtext.ui.refactoring.IReferenceUpdater;

import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.google.inject.Inject;

/**
 * Abstract base class to update the references to renamed elements.
 * 
 * Sorts all references by project and uses a separate resource set for each project to assert proper initialization.
 * Updates are performed in clusters of 20 (default) referring resources. 
 * 
 * @author Jan Koehnlein - Initial contribution and API
 * @author Holger Schill
 */
public abstract class AbstractReferenceUpdater implements IReferenceUpdater {

    @Inject
    private ReferenceDescriptionSorter sorter;

    @Inject
    private RefactoringResourceSetProvider resourceSetProvider;

    @Override
    public void createReferenceUpdates(ElementRenameArguments elementRenameArguments,
            Iterable<IReferenceDescription> referenceDescriptions, IRefactoringUpdateAcceptor updateAcceptor,
            IProgressMonitor monitor) {
        RefactoringResourceSetProvider provider = elementRenameArguments.getResourceSetProvider();
        createReferenceUpdates(elementRenameArguments, referenceDescriptions, updateAcceptor,
                provider != null ? provider : resourceSetProvider, monitor);
    }

    protected void createReferenceUpdates(ElementRenameArguments elementRenameArguments,
            Iterable<IReferenceDescription> referenceDescriptions, IRefactoringUpdateAcceptor updateAcceptor,
            RefactoringResourceSetProvider resourceSetProvider, IProgressMonitor monitor) {
        SubMonitor progress = SubMonitor.convert(monitor, 100);
        progress.beginTask("Sort references by project", 1);
        Multimap<IProject, IReferenceDescription> project2references = sorter.sortByProject(referenceDescriptions);
        SubMonitor allProjectsProgress = progress.newChild(98).setWorkRemaining(project2references.keySet().size());
        for (IProject project : project2references.keySet()) {
            if (allProjectsProgress.isCanceled()) {
                throw new OperationCanceledException();
            }
            Multimap<URI, IReferenceDescription> resource2references = sorter
                    .sortByResource(project2references.get(project));
            ResourceSet resourceSet = resourceSetProvider.get(project);
            StatusWrapper status = updateAcceptor.getRefactoringStatus();
            createClusteredReferenceUpdates(elementRenameArguments, resource2references, resourceSet,
                    updateAcceptor, status, allProjectsProgress.newChild(1));
        }
    }

    protected void createClusteredReferenceUpdates(ElementRenameArguments elementRenameArguments,
            Multimap<URI, IReferenceDescription> resource2references, ResourceSet resourceSet,
            IRefactoringUpdateAcceptor updateAcceptor, StatusWrapper status, IProgressMonitor monitor) {
        SubMonitor progress = SubMonitor.convert(monitor, resource2references.keySet().size() + 1);
        if (loadTargetResources(resourceSet, elementRenameArguments, status, progress.newChild(1))) {
            if (getClusterSize() > 0) {
                Set<Resource> targetResources = newHashSet(resourceSet.getResources());
                Multimap<URI, IReferenceDescription> cluster = HashMultimap.create();
                SubMonitor clusterMonitor = progress.newChild(1);
                for (URI referringResourceURI : resource2references.keySet()) {
                    cluster.putAll(referringResourceURI, resource2references.get(referringResourceURI));
                    if (cluster.keySet().size() == getClusterSize()) {
                        unloadNonTargetResources(resourceSet, targetResources);
                        createReferenceUpdatesForCluster(elementRenameArguments, cluster, resourceSet,
                                updateAcceptor, status, clusterMonitor);
                        cluster.clear();
                    }
                }
                if (!cluster.isEmpty()) {
                    unloadNonTargetResources(resourceSet, targetResources);
                    createReferenceUpdatesForCluster(elementRenameArguments, cluster, resourceSet, updateAcceptor,
                            status, clusterMonitor);
                }
            } else {
                createReferenceUpdatesForCluster(elementRenameArguments, resource2references, resourceSet,
                        updateAcceptor, status, progress.newChild(resource2references.keySet().size()));
            }
        }
    }

    protected void unloadNonTargetResources(ResourceSet resourceSet, Set<Resource> targetResources) {
        for (Resource resource : newArrayList(resourceSet.getResources())) {
            if (!targetResources.contains(resource)) {
                resource.unload();
                resourceSet.getResources().remove(resource);
            }
        }
    }

    protected int getClusterSize() {
        return 20;
    }

    protected void createReferenceUpdatesForCluster(ElementRenameArguments elementRenameArguments,
            Multimap<URI, IReferenceDescription> resource2references, ResourceSet resourceSet,
            IRefactoringUpdateAcceptor updateAcceptor, StatusWrapper status, IProgressMonitor monitor) {
        SubMonitor progress = SubMonitor.convert(monitor, 100);
        List<URI> unloadableResources = loadReferringResources(resourceSet, resource2references.keySet(), status,
                progress.newChild(10));
        if (progress.isCanceled()) {
            throw new OperationCanceledException();
        }
        for (URI unloadableResouce : unloadableResources)
            resource2references.removeAll(unloadableResouce);
        List<IReferenceDescription> unresolvableReferences = resolveReferenceProxies(resourceSet,
                resource2references.values(), status, progress.newChild(70));
        if (progress.isCanceled()) {
            throw new OperationCanceledException();
        }
        for (IReferenceDescription unresolvableReference : unresolvableReferences) {
            URI unresolvableReferringResource = unresolvableReference.getSourceEObjectUri().trimFragment();
            resource2references.remove(unresolvableReferringResource, unresolvableReference);
        }
        elementRenameArguments.getRenameStrategy().applyDeclarationChange(elementRenameArguments.getNewName(),
                resourceSet);
        if (progress.isCanceled()) {
            throw new OperationCanceledException();
        }
        createReferenceUpdates(elementRenameArguments, resource2references, resourceSet, updateAcceptor,
                progress.newChild(20));
        if (progress.isCanceled()) {
            throw new OperationCanceledException();
        }
        elementRenameArguments.getRenameStrategy().revertDeclarationChange(resourceSet);
    }

    protected List<IReferenceDescription> resolveReferenceProxies(ResourceSet resourceSet,
            Collection<IReferenceDescription> values, StatusWrapper status, IProgressMonitor monitor) {
        List<IReferenceDescription> unresolvedDescriptions = null;
        for (IReferenceDescription referenceDescription : values) {
            if (monitor.isCanceled()) {
                throw new OperationCanceledException();
            }
            EObject sourceEObject = resourceSet.getEObject(referenceDescription.getSourceEObjectUri(), true);
            if (sourceEObject == null) {
                handleCannotLoadReferringElement(referenceDescription, status);
            } else {
                // this should not be necessary. see https://bugs.eclipse.org/bugs/show_bug.cgi?id=385408 
                if (sourceEObject.eIsProxy()) {
                    sourceEObject = EcoreUtil.resolve(sourceEObject, sourceEObject.eResource());
                    if (sourceEObject.eIsProxy()) {
                        handleCannotLoadReferringElement(referenceDescription, status);
                    }
                }
                EObject resolvedReference = resolveReference(sourceEObject, referenceDescription);
                if (resolvedReference == null || resolvedReference.eIsProxy())
                    handleCannotResolveExistingReference(sourceEObject, referenceDescription, status);
                else
                    continue;
            }
            if (unresolvedDescriptions == null)
                unresolvedDescriptions = newArrayList();
            unresolvedDescriptions.add(referenceDescription);
        }
        return (unresolvedDescriptions == null) ? Collections.<IReferenceDescription>emptyList()
                : unresolvedDescriptions;
    }

    protected EObject resolveReference(EObject referringElement, IReferenceDescription referenceDescription) {
        Object resolvedValue = referringElement.eGet(referenceDescription.getEReference());
        if (referenceDescription.getEReference().isMany()) {
            List<?> list = (List<?>) resolvedValue;
            resolvedValue = list.get(referenceDescription.getIndexInList());
        }
        return (EObject) resolvedValue;
    }

    protected void handleCannotLoadReferringElement(IReferenceDescription referenceDescription,
            StatusWrapper status) {
        status.add(ERROR, "Cannot find referring element {0}.\nMaybe the index is be corrupt. Consider a rebuild.",
                referenceDescription.getSourceEObjectUri());
    }

    protected void handleCannotResolveExistingReference(EObject sourceEObject,
            IReferenceDescription referenceDescription, StatusWrapper status) {
        status.add(ERROR, "Cannot resolve existing reference.\nMaybe the index is be corrupt. Consider a rebuild.",
                referenceDescription.getSourceEObjectUri());
    }

    protected abstract void createReferenceUpdates(ElementRenameArguments elementRenameArguments,
            Multimap<URI, IReferenceDescription> resource2references, ResourceSet resourceSet,
            IRefactoringUpdateAcceptor updateAcceptor, IProgressMonitor monitor);

    protected boolean loadTargetResources(ResourceSet resourceSet, ElementRenameArguments elementRenameArguments,
            StatusWrapper status, IProgressMonitor monitor) {
        boolean isSuccess = true;
        for (URI renamedElementURI : elementRenameArguments.getRenamedElementURIs()) {
            if (monitor.isCanceled()) {
                throw new OperationCanceledException();
            }
            EObject renamedElement = resourceSet.getEObject(renamedElementURI, true);
            if (renamedElement == null || renamedElement.eIsProxy()) {
                status.add(ERROR, "Cannot load target element {0}.", renamedElementURI);
                isSuccess = false;
            }
        }
        return isSuccess;
    }

    protected List<URI> loadReferringResources(ResourceSet resourceSet, Iterable<URI> referringResourceURIs,
            StatusWrapper status, IProgressMonitor monitor) {
        List<URI> unloadableResources = null;
        for (URI referringResourceURI : referringResourceURIs) {
            if (monitor.isCanceled()) {
                throw new OperationCanceledException();
            }
            Resource referringResource = resourceSet.getResource(referringResourceURI, true);
            if (referringResource == null) {
                status.add(ERROR, "Could not load referring resource ", referringResourceURI);
                if (unloadableResources == null)
                    unloadableResources = newArrayList();
                unloadableResources.add(referringResourceURI);
            }
        }
        return unloadableResources == null ? Collections.<URI>emptyList() : unloadableResources;
    }

}