/*******************************************************************************
* Copyright (c) 2012 Pivotal Software, Inc.
* 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:
* Pivotal Software, Inc. - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.boot.dash.util;
import static org.springframework.ide.eclipse.boot.launch.BootLaunchConfigurationDelegate.isHiddenFromBootDash;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationListener;
import org.eclipse.debug.core.ILaunchConfigurationType;
import org.eclipse.debug.core.ILaunchManager;
import org.springframework.ide.eclipse.boot.core.BootActivator;
import org.springframework.ide.eclipse.boot.dash.model.BootProjectDashElement;
import org.springframework.ide.eclipse.boot.launch.BootLaunchConfigurationDelegate;
import org.springsource.ide.eclipse.commons.livexp.core.AsyncLiveExpression.AsyncMode;
import org.springsource.ide.eclipse.commons.livexp.core.LiveSetVariable;
import org.springsource.ide.eclipse.commons.livexp.core.ObservableSet;
import org.springsource.ide.eclipse.commons.livexp.ui.Disposable;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSet.Builder;
/**
* This class is responsible of maintaining a map of {@link ILaunchConfiguration}
* that represent the children of {@link BootProjectDashElement}s.
*
* @author Kris De Volder
*/
public class LaunchConfigurationTracker implements Disposable {
private final AtomicBoolean initialized = new AtomicBoolean(false);
private final ILaunchManager launchManager;
private final ILaunchConfigurationType launchType;
private final Map<IProject, LiveSetVariable<ILaunchConfiguration>> configs = new HashMap<>();
private ILaunchConfigurationListener launchConfListener;
private Job refreshJob;
public LaunchConfigurationTracker(String launchTypeId, ILaunchManager launchManager) {
this.launchManager = launchManager;
this.launchType = launchManager.getLaunchConfigurationType(launchTypeId);
}
public LaunchConfigurationTracker(String typeId) {
this(typeId, DebugPlugin.getDefault().getLaunchManager());
}
private void init() {
if (initialized.compareAndSet(false, true)) {
launchManager.addLaunchConfigurationListener(launchConfListener = new ILaunchConfigurationListener() {
@Override
public void launchConfigurationRemoved(ILaunchConfiguration configuration) {
//Careful, do not call 'refreshIfNeeded' here. It is impossible to determine
// the type of a deleted config (eclipse will throw an exeption if you try)
//So we have to call refresh directly here.
refresh();
}
@Override
public void launchConfigurationChanged(ILaunchConfiguration configuration) {
refreshIfNeeded(configuration);
}
@Override
public void launchConfigurationAdded(ILaunchConfiguration configuration) {
refreshIfNeeded(configuration);
}
private void refreshIfNeeded(ILaunchConfiguration configuration) {
try {
if (configuration!=null && launchType.equals(configuration.getType())) {
refresh();
}
} catch (CoreException e) {
BootActivator.log(e);
}
}
});
refresh();
}
}
private void refresh() {
refreshJob().schedule();
}
private synchronized Job refreshJob() {
if (refreshJob==null) {
refreshJob = new Job("Refresh Launch Conf Boot Dash Elements") {
protected IStatus run(IProgressMonitor arg0) {
Map<IProject, Set<ILaunchConfiguration>> newSets = new HashMap<>();
synchronized (LaunchConfigurationTracker.this) {
for (IProject oldProject : configs.keySet()) {
//enure there's at least an empty set for any relevant project
//in the newSets map:
getSet(newSets, oldProject);
}
}
for (ILaunchConfiguration conf : getRelevantConfs()) {
IProject project = BootLaunchConfigurationDelegate.getProject(conf);
if (project!=null) {
add(newSets, project, conf);
}
}
for (Entry<IProject, Set<ILaunchConfiguration>> newEntry : newSets.entrySet()) {
IProject newProject = newEntry.getKey();
LiveSetVariable<ILaunchConfiguration> liveset = getVar(newProject);
liveset.replaceAll(newEntry.getValue());
}
return Status.OK_STATUS;
}
};
refreshJob.setSystem(true);
}
return refreshJob;
}
private void add(Map<IProject, Set<ILaunchConfiguration>> index, IProject project, ILaunchConfiguration conf) {
getSet(index, project).add(conf);
}
private Set<ILaunchConfiguration> getSet(Map<IProject, Set<ILaunchConfiguration>> index,
IProject project) {
Set<ILaunchConfiguration> elements = index.get(project);
if (elements==null) {
index.put(project, elements = new HashSet<>());
}
return elements;
}
public ObservableSet<ILaunchConfiguration> getConfigs(IProject project) {
init();
return getVar(project);
}
private synchronized LiveSetVariable<ILaunchConfiguration> getVar(IProject project) {
LiveSetVariable<ILaunchConfiguration> existing = configs.get(project);
if (existing==null) {
configs.put(project, existing = new LiveSetVariable<>(AsyncMode.SYNC));
}
return existing;
}
private ImmutableSet<ILaunchConfiguration> getRelevantConfs() {
try {
ILaunchConfiguration[] allConfigs = launchManager.getLaunchConfigurations(launchType);
Builder<ILaunchConfiguration> builder = ImmutableSet.builder();
for (ILaunchConfiguration c : allConfigs) {
if (isRelevant(c)) {
builder.add(c);
}
}
return builder.build();
} catch (Exception e) {
BootActivator.log(e);
return ImmutableSet.of();
}
}
private boolean isRelevant(ILaunchConfiguration c) {
//Note: no need to check the launch conf type as only configs of the right type are passed in here.
return !isHiddenFromBootDash(c);
}
@Override
public synchronized void dispose() {
if (launchConfListener!=null) {
launchManager.removeLaunchConfigurationListener(launchConfListener);
launchConfListener = null;
}
}
}