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

Java tutorial

Introduction

Here is the source code for org.eclipse.xtext.ui.refactoring.impl.ReferenceUpdaterDispatcher.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.Maps.*;
import static org.eclipse.ltk.core.refactoring.RefactoringStatus.*;

import java.util.Map;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.xtext.findReferences.IReferenceFinder;
import org.eclipse.xtext.resource.IReferenceDescription;
import org.eclipse.xtext.resource.IResourceDescriptions;
import org.eclipse.xtext.resource.IResourceServiceProvider;
import org.eclipse.xtext.resource.IResourceServiceProvider.Registry;
import org.eclipse.xtext.resource.impl.DefaultReferenceDescription;
import org.eclipse.xtext.ui.editor.findrefs.ResourceAccess;
import org.eclipse.xtext.ui.editor.findrefs.TargetURIConverter;
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;
import com.google.inject.Provider;

/**
 * Finds all references to renamed elements and dispatches to the {@link IReferenceUpdater}of the referring languages
 * to calculate the updates.
 * 
 * @author Jan Koehnlein - Initial contribution and API
 */
public class ReferenceUpdaterDispatcher {

    @Inject
    private IReferenceFinder referenceFinder;

    @Inject
    private IResourceServiceProvider.Registry resourceServiceProviderRegistry;

    @Inject
    private TargetURIConverter targetURIConverter;

    @Inject
    private Provider<ResourceAccess> resourceAccessProvider;

    @Inject
    private IResourceDescriptions indexData;

    public void createReferenceUpdates(ElementRenameArguments elementRenameArguments, ResourceSet resourceSet,
            IRefactoringUpdateAcceptor updateAcceptor, IProgressMonitor monitor) {
        SubMonitor progress = SubMonitor.convert(monitor, "Updating references", 100);
        ResourceAccess resourceAccess = resourceAccessProvider.get();
        resourceAccess.registerResourceSet(resourceSet);

        ReferenceDescriptionAcceptor referenceDescriptionAcceptor = createFindReferenceAcceptor(updateAcceptor);
        referenceFinder.findAllReferences(
                targetURIConverter.fromIterable(elementRenameArguments.getRenamedElementURIs()), resourceAccess,
                indexData, referenceDescriptionAcceptor, progress.newChild(2));
        Multimap<IReferenceUpdater, IReferenceDescription> updater2descriptions = referenceDescriptionAcceptor
                .getReferenceUpdater2ReferenceDescriptions();
        SubMonitor updaterProgress = progress.newChild(98).setWorkRemaining(updater2descriptions.keySet().size());
        for (IReferenceUpdater referenceUpdater : updater2descriptions.keySet()) {
            if (updaterProgress.isCanceled())
                return;
            referenceUpdater.createReferenceUpdates(elementRenameArguments,
                    updater2descriptions.get(referenceUpdater), updateAcceptor, updaterProgress.newChild(1));
        }
    }

    protected ReferenceDescriptionAcceptor createFindReferenceAcceptor(IRefactoringUpdateAcceptor updateAcceptor) {
        return new ReferenceDescriptionAcceptor(resourceServiceProviderRegistry,
                updateAcceptor.getRefactoringStatus());
    }

    public static class ReferenceDescriptionAcceptor implements IReferenceFinder.Acceptor {

        private Map<IResourceServiceProvider, IReferenceUpdater> provider2updater = newHashMap();
        private Multimap<IReferenceUpdater, IReferenceDescription> updater2refs = HashMultimap.create();

        private StatusWrapper status;
        private final Registry resourceServiceProviderRegistry;

        public ReferenceDescriptionAcceptor(IResourceServiceProvider.Registry resourceServiceProviderRegistry,
                StatusWrapper status) {
            this.resourceServiceProviderRegistry = resourceServiceProviderRegistry;
            this.status = status;
        }

        @Override
        public void accept(IReferenceDescription referenceDescription) {
            if (referenceDescription.getSourceEObjectUri() == null
                    || referenceDescription.getTargetEObjectUri() == null
                    || referenceDescription.getEReference() == null) {
                handleCorruptReferenceDescription(referenceDescription, status);
            } else {
                URI sourceResourceURI = referenceDescription.getSourceEObjectUri().trimFragment();
                IReferenceUpdater referenceUpdater = getReferenceUpdater(sourceResourceURI);
                if (referenceUpdater == null)
                    handleNoReferenceUpdater(sourceResourceURI, status);
                else
                    updater2refs.put(referenceUpdater, referenceDescription);
            }
        }

        @Override
        public void accept(EObject source, URI sourceURI, EReference eReference, int index, EObject targetOrProxy,
                URI targetURI) {
            accept(new DefaultReferenceDescription(sourceURI, targetURI, eReference, index, null));
        }

        protected void handleNoReferenceUpdater(URI sourceResourceURI, StatusWrapper status) {
            status.add(WARNING,
                    "References from {0} will not be updated as the language has not registered an IReferenceUpdater",
                    sourceResourceURI);
        }

        protected void handleCorruptReferenceDescription(IReferenceDescription referenceDescription,
                StatusWrapper status) {
            status.add(ERROR,
                    "Xtext index contains invalid entries. It is suggested to perform a workspace refresh and a clean build.");
        }

        protected IReferenceUpdater getReferenceUpdater(URI sourceResourceURI) {
            //TODO Why do we cache the IReferenceUpdater here?
            IResourceServiceProvider resourceServiceProvider = resourceServiceProviderRegistry
                    .getResourceServiceProvider(sourceResourceURI);
            if (resourceServiceProvider == null)
                return null;
            IReferenceUpdater referenceUpdater = provider2updater.get(resourceServiceProvider);
            if (referenceUpdater == null) {
                referenceUpdater = resourceServiceProvider.get(OptionalReferenceUpdaterProxy.class).get();
                if (referenceUpdater != null)
                    provider2updater.put(resourceServiceProvider, referenceUpdater);
            }
            return referenceUpdater;
        }

        public Multimap<IReferenceUpdater, IReferenceDescription> getReferenceUpdater2ReferenceDescriptions() {
            return updater2refs;
        }
    }

    protected static class OptionalReferenceUpdaterProxy {
        @Inject(optional = true)
        private IReferenceUpdater referenceUpdater;

        public IReferenceUpdater get() {
            return referenceUpdater;
        }
    }
}