eu.numberfour.n4js.ui.workingsets.GitRepositoryAwareWorkingSetManager.java Source code

Java tutorial

Introduction

Here is the source code for eu.numberfour.n4js.ui.workingsets.GitRepositoryAwareWorkingSetManager.java

Source

/**
 * Copyright (c) 2016 NumberFour AG.
 * 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:
 *   NumberFour AG - Initial API and implementation
 */
package eu.numberfour.n4js.ui.workingsets;

import static com.google.common.collect.FluentIterable.from;
import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Maps.toMap;
import static com.google.common.collect.Sets.newHashSet;
import static java.io.File.pathSeparator;

import java.io.File;
import java.net.URI;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener;
import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.egit.core.Activator;
import org.eclipse.egit.core.RepositoryCache;
import org.eclipse.egit.core.RepositoryUtil;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.swt.graphics.Image;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.BundleListener;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Optional;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.MapDifference;
import com.google.common.collect.Maps;

import eu.numberfour.n4js.ui.ImageDescriptorCache.ImageRef;

/**
 * Working set manager based on Git repositories.
 */
@SuppressWarnings("restriction")
public class GitRepositoryAwareWorkingSetManager extends WorkingSetManagerImpl implements IDeferredInitializer {

    private final RepositoryCache repositoryCache;
    private final IPreferenceChangeListener repositoryChangeListener;

    private boolean deferredInitializerSucceeded = false;

    /**
     * Sole constructor for creating the working set manager. Internally initializes the cache for repositories.
     */
    public GitRepositoryAwareWorkingSetManager() {
        repositoryCache = Activator.getDefault().getRepositoryCache(); // might not be initialized yet.
        repositoryChangeListener = new IPreferenceChangeListener() {

            @SuppressWarnings("deprecation")
            // keep deprecated RepositoryUtil.PREFS_DIRECTORIES for backward-compatibility
            @Override
            public void preferenceChange(final PreferenceChangeEvent event) {
                if (!RepositoryUtil.PREFS_DIRECTORIES_REL.equals(event.getKey())
                        && !RepositoryUtil.PREFS_DIRECTORIES.equals(event.getKey())) {
                    return;
                }

                if (!orderedWorkingSetIds.isEmpty() && !visibleWorkingSetIds.isEmpty()) {

                    MapDifference<String, String> diff = calculateDifference(event);
                    if (!diff.areEqual()) {

                        // Deletions
                        final Set<String> deletions = diff.entriesOnlyOnLeft().keySet();
                        for (String deletedUrl : deletions) {
                            orderedWorkingSetIds.remove(deletedUrl);
                            visibleWorkingSetIds.remove(deletedUrl);
                        }

                        // Additions
                        final Set<String> additions = diff.entriesOnlyOnRight().keySet();
                        for (String addedUrl : additions) {
                            orderedWorkingSetIds.add(addedUrl);
                            visibleWorkingSetIds.add(addedUrl);
                        }

                    }

                }

                discardWorkingSetCaches();
                saveState(new NullProgressMonitor());

                WorkingSetManagerBroker workingSetManagerBroker = getWorkingSetManagerBroker();
                if (workingSetManagerBroker.isWorkingSetTopLevel()) {
                    final WorkingSetManager activeManager = workingSetManagerBroker.getActiveManager();
                    if (activeManager != null) {
                        if (activeManager.getId().equals(getId())) {
                            workingSetManagerBroker.refreshNavigator();
                        }
                    }
                }

            }

            private MapDifference<String, String> calculateDifference(PreferenceChangeEvent event) {
                String oldValue = Strings.nullToEmpty((String) event.getOldValue());
                String newValue = Strings.nullToEmpty((String) event.getNewValue());

                Map<String, String> oldMappings = toMap(newHashSet(Splitter.on(pathSeparator).split(oldValue)),
                        i -> i);
                Map<String, String> newMappings = toMap(newHashSet(Splitter.on(pathSeparator).split(newValue)),
                        i -> i);

                return Maps.difference(oldMappings, newMappings);

            }

        };

        final IEclipsePreferences gitNode = InstanceScope.INSTANCE.getNode(Activator.getPluginId());
        gitNode.addPreferenceChangeListener(repositoryChangeListener);

        final BundleContext context = Activator.getDefault().getBundle().getBundleContext();
        context.addBundleListener(new BundleListener() {

            @Override
            public void bundleChanged(final BundleEvent event) {
                if (BundleEvent.STOPPING == event.getType()) {
                    gitNode.removePreferenceChangeListener(repositoryChangeListener);
                }
            }

        });
    }

    @Override
    public String getLabel() {
        return "Git Repository";
    }

    @Override
    public Optional<Image> getImage() {
        return ImageRef.REPOSITORY.asImage();
    }

    @Override
    protected List<WorkingSet> initializeWorkingSets() {
        final Collection<Repository> repositories = newArrayList(repositoryCache.getAllRepositories());

        // we cannot query the cache about its state
        // so if we get no repository at all, we assume it has not been initialized yet
        deferredInitializerSucceeded = !repositories.isEmpty();

        repositories.add(null); // For 'Other Projects'.
        return newArrayList(
                from(repositories).transform(repository -> new GitRepositoryWorkingSet(repository, this)));
    }

    @Override
    public boolean isInitializationRequired() {
        return !deferredInitializerSucceeded;
    }

    @Override
    public boolean lateInit() {

        if (deferredInitializerSucceeded) {
            return true;
        }

        restoreState(new NullProgressMonitor());

        return deferredInitializerSucceeded;
    }

    /**
     * Working set for grouping projects based on the attached local Git repository.
     */
    public static final class GitRepositoryWorkingSet extends DefaultWorkingSetImpl {

        private final String rootUri;
        private final String name;

        private static String repositoryToId(Repository repository) {
            if (null == repository) {
                return OTHERS_WORKING_SET_ID;
            }
            return toUriString(repository.getDirectory().getParentFile().toURI());
        }

        /**
         * Creates a new working set instance with the optional {@link Repository Git repository} and the container
         * working set manager.
         *
         * @param repository
         *            the associated Git repository. Could be {@code null} if the working set is for
         *            {@link WorkingSet#OTHERS_WORKING_SET_ID <em>'Other Project'</em>} purposes.
         * @param manager
         *            the container manager.
         */
        @VisibleForTesting
        public GitRepositoryWorkingSet(/* nullable */ final Repository repository,
                final WorkingSetManager manager) {
            super(repositoryToId(repository), manager);
            if (repository == null) {
                rootUri = null;
                name = OTHERS_WORKING_SET_ID;
            } else {
                final File directory = repository.getDirectory().getParentFile();
                rootUri = toUriString(directory.toURI());
                name = directory.getName();
            }
        }

        @Override
        public boolean apply(final IProject project) {
            return rootUri != null && toUriString(project.getLocationURI()).startsWith(rootUri);
        }

        @Override
        public String getName() {
            return name;
        }

        /**
         * Returns with the {@link URI#toString()} of the argument. Trims the trailing forward slash if any.
         */
        private static String toUriString(final URI uri) {
            final String uriString = uri.toString();
            final int length = uriString.length();
            if (uriString.charAt(length - 1) == '/') {
                return uriString.substring(0, length - 1);
            }
            return uriString;
        }

    }

}