<commit_before>/*******************************************************************************
 * Copyright (c) 2013 GoPivotal, 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:
 *     GoPivotal, Inc. - initial API and implementation
 *******************************************************************************/
package org.springsource.ide.eclipse.commons.ui.launch;

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;

import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.model.IProcess;

/**
 * A variation of LiveProcessTracker that retains launch configurations even
 * after all the associated processes have been terminated. (Thus allowing to
 * relaunch.
 *
 * @author <NAME>
 */
public class LiveAndDeadProcessTracker extends LaunchList {

	/*
	 * The code in this class seems more complicated than it should be. The main source of
	 * complication is dealing with cases where tracked launch configs have been renamed
	 * or deleted. Ideally we would update the lists based on a ILaunchConfigurationListener
	 * any time this happens. Unfortunately there's no 'renamed' event. Instead we
	 * get disconnected 'delete' and 'create' events.
	 *
	 * What we do instead is check the elements returned by accessor methods 'getLast' and
	 * 'getLaunches' on every call for 'broken' elements. Any 'broken' elements are dealt with either
	 * by 'repairing' them if we can determine what it was renamed to, or deleting them
	 * if we can not.
	 *
	 * Further complications are due to the fact that LinkedHashMap has no 'getLast' method.
	 * We keep the 'last' element in a separate field. Keeping this field in sync
	 * with the actual elements in the map requires some effort.
	 */

	public class ProcessItem extends Item {

		private final IProcess process; //This is so we can recover the configuration
		                          // for processes after the conf has been renamed.
		                          // Rename = delete + create.

		public ProcessItem(ILaunchConfiguration conf, String mode, IProcess process) {
			super(conf, mode);
			this.process = process;
		}

		public ProcessItem setConf(ILaunchConfiguration newConf) {
			return new ProcessItem(newConf, mode, process);
		}

	}

	private static final boolean DEBUG = false; //(""+Platform.getLocation()).contains("kdvolder");

	private static LiveAndDeadProcessTracker instance;
	public synchronized static LaunchList getInstance() {
		if (instance==null) {
			instance = new LiveAndDeadProcessTracker();
		}
		return instance;
	}

	private final LinkedHashMap<String, ProcessItem> configs = new LinkedHashMap<String, ProcessItem>();
	private ProcessItem last = null;

	public LiveAndDeadProcessTracker() {
		//DebugPlugin.getDefault().getLaunchManager().addLaunchConfigurationListener(this);
	}

	@Override
	protected void processTerminated(IProcess process) {
		//nothing... we keep the dead ones too!
	}

	@Override
	protected void processCreated(IProcess process) {
		synchronized (process) {
			ILaunch l = process.getLaunch();
			if (l!=null) {
				ILaunchConfiguration c = l.getLaunchConfiguration();
				if (c!=null) {
					last = new ProcessItem(c, l.getLaunchMode(), process);
					configs.remove(last.getName()); //so the element moves to the end being now the 'most' recent.
					configs.put(last.getName(), last);
				}
			}
		}
		fireChangeEvent();
	}

	@Override
	public synchronized Item getLast() {
		if (last!=null) {
			if (last.conf.exists()) {
				return last;
			}
			//might be conf delete or renamed
			last = findRenamed(last);
			if (last==null) {
				//last was deleted... find the 'previous last'.
				// It's a shame we have to iterate the whole collection to find the last one...
				// but there's no other way with a LinkedHashMap
				for (Item newLast : getLaunches()) {
					last = (ProcessItem)newLast;
				}
			}
		}
		return last;
	}

	@Override
	public synchronized Collection<Item> getLaunches() {
//		debug(">>> getLaunches");

		ArrayList<String> names = new ArrayList<String>(configs.keySet());
		for (String name : names) {
//			debug("name (key)  = "+name);
			ProcessItem item = configs.get(name);
//			debug("name (item) = "+item.getName());
			ILaunchConfiguration conf = item.conf;
			if (conf.exists()) {
//				debug("exists: "+conf.getName());
				continue;
			}
			// could be renamed or deleted
			ProcessItem renamedItem = findRenamed(item);
			if (renamedItem!=null) {
//				debug("renamed: "+name+" -> "+renamedItem.getName());
				configs.remove(item.getName());
				configs.put(renamedItem.getName(), renamedItem);
				continue;
			}
			//config no longer valid probably deleted (or renamed, but couldn't figure out the new name).
//			debug("deleted: "+name);
			configs.remove(item.getName());
		}
		ArrayList<Item> items = new ArrayList<LaunchList.Item>(configs.values());
//		debug("<<< getLaunches # "+items.size());
		return items;
	}

	/**
	 * Try to 'fix' a renamed element. This only works if we can recover the
	 * new config via its association with the process. This association may
	 * no longer exist if the launch was deregistered from the debugger already.
	 * So this is a 'best effort' implementation.
	 *
	 * @return Fixed element or null if a corresponding renamed config couldn't
	 *      be found.
	 */
	private ProcessItem findRenamed(ProcessItem item) {
		ILaunch l = item.process.getLaunch();
		if (l!=null) {
			ILaunchConfiguration renamedConf = l.getLaunchConfiguration();
			if (renamedConf!=null && renamedConf.exists()) {
				return item.setConf(renamedConf);
			}
		}
		return null;
	}

	private void debug(String string) {
		if (DEBUG) {
			System.out.println(string);
		}
	}

}<commit_msg>Fix Relaunch button so it works for LaunchConfigurationWorking copies (i.e. when a launch shortcut only create non-persisted working copy).<commit_after>/*******************************************************************************
 * Copyright (c) 2013 GoPivotal, 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:
 *     GoPivotal, Inc. - initial API and implementation
 *******************************************************************************/
package org.springsource.ide.eclipse.commons.ui.launch;

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;

import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.model.IProcess;

/**
 * A variation of LiveProcessTracker that retains launch configurations even
 * after all the associated processes have been terminated. (Thus allowing to
 * relaunch.
 *
 * @author <NAME>
 */
public class LiveAndDeadProcessTracker extends LaunchList {

	/*
	 * The code in this class seems more complicated than it should be. The main source of
	 * complication is dealing with cases where tracked launch configs have been renamed
	 * or deleted. Ideally we would update the lists based on a ILaunchConfigurationListener
	 * any time this happens. Unfortunately there's no 'renamed' event. Instead we
	 * get disconnected 'delete' and 'create' events.
	 *
	 * What we do instead is check the elements returned by accessor methods 'getLast' and
	 * 'getLaunches' on every call for 'broken' elements. Any 'broken' elements are dealt with either
	 * by 'repairing' them if we can determine what it was renamed to, or deleting them
	 * if we can not.
	 *
	 * Further complications are due to the fact that LinkedHashMap has no 'getLast' method.
	 * We keep the 'last' element in a separate field. Keeping this field in sync
	 * with the actual elements in the map requires some effort.
	 */

	public class ProcessItem extends Item {

		private final IProcess process; //This is so we can recover the configuration
		                          // for processes after the conf has been renamed.
		                          // Rename = delete + create.

		public ProcessItem(ILaunchConfiguration conf, String mode, IProcess process) {
			super(conf, mode);
			this.process = process;
		}

		public ProcessItem setConf(ILaunchConfiguration newConf) {
			return new ProcessItem(newConf, mode, process);
		}

	}

	private static final boolean DEBUG = false;// (""+Platform.getLocation()).contains("kdvolder");

	private static LiveAndDeadProcessTracker instance;
	public synchronized static LaunchList getInstance() {
		if (instance==null) {
			instance = new LiveAndDeadProcessTracker();
		}
		return instance;
	}

	private final LinkedHashMap<String, ProcessItem> configs = new LinkedHashMap<String, ProcessItem>();
	private ProcessItem last = null;

	public LiveAndDeadProcessTracker() {
		//DebugPlugin.getDefault().getLaunchManager().addLaunchConfigurationListener(this);
	}

	@Override
	protected void processTerminated(IProcess process) {
		//nothing... we keep the dead ones too!
	}

	@Override
	protected void processCreated(IProcess process) {
		synchronized (process) {
			ILaunch l = process.getLaunch();
			if (l!=null) {
				ILaunchConfiguration c = l.getLaunchConfiguration();
				if (c!=null) {
					last = new ProcessItem(c, l.getLaunchMode(), process);
					configs.remove(last.getName()); //so the element moves to the end being now the 'most' recent.
					configs.put(last.getName(), last);
				}
			}
		}
		fireChangeEvent();
	}

	private boolean exists(ILaunchConfiguration conf) {
		//Assume working copyies exist as long as we have a reference to them.
		return conf.exists() || conf.isWorkingCopy();
	}


	@Override
	public synchronized Item getLast() {
		if (last!=null) {
			if (exists(last.conf)) {
				return last;
			}
			//might be conf delete or renamed
			last = findRenamed(last);
			if (last==null) {
				//last was deleted... find the 'previous last'.
				// It's a shame we have to iterate the whole collection to find the last one...
				// but there's no other way with a LinkedHashMap
				for (Item newLast : getLaunches()) {
					last = (ProcessItem)newLast;
				}
			}
		}
		return last;
	}

	@Override
	public synchronized Collection<Item> getLaunches() {
		//debug(">>> getLaunches");

		ArrayList<String> names = new ArrayList<String>(configs.keySet());
		for (String name : names) {
//			debug("name (key)  = "+name);
			ProcessItem item = configs.get(name);
//			debug("name (item) = "+item.getName());
			ILaunchConfiguration conf = item.conf;
			if (exists(conf)) {
//				debug("exists: "+conf.getName());
				continue;
			}
			// could be renamed or deleted
			ProcessItem renamedItem = findRenamed(item);
			if (renamedItem!=null) {
//				debug("renamed: "+name+" -> "+renamedItem.getName());
				configs.remove(item.getName());
				configs.put(renamedItem.getName(), renamedItem);
				continue;
			}
			//config no longer valid probably deleted (or renamed, but couldn't figure out the new name).
//			debug("deleted: "+name);
			configs.remove(item.getName());
		}
		ArrayList<Item> items = new ArrayList<LaunchList.Item>(configs.values());
//		debug("<<< getLaunches # "+items.size());
		return items;
	}

	/**
	 * Try to 'fix' a renamed element. This only works if we can recover the
	 * new config via its association with the process. This association may
	 * no longer exist if the launch was deregistered from the debugger already.
	 * So this is a 'best effort' implementation.
	 *
	 * @return Fixed element or null if a corresponding renamed config couldn't
	 *      be found.
	 */
	private ProcessItem findRenamed(ProcessItem item) {
		ILaunch l = item.process.getLaunch();
		if (l!=null) {
			ILaunchConfiguration renamedConf = l.getLaunchConfiguration();
			if (renamedConf!=null && exists(renamedConf)) {
				return item.setConf(renamedConf);
			}
		}
		return null;
	}

	private void debug(String string) {
		if (DEBUG) {
			System.out.println(string);
		}
	}

}
{'fraction_non_alphanumeric': 0.1285877364644488, 'fraction_numerical': 0.001304631441617743, 'mean_word_length': 3.731867283950617, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3879236', 'n_tokens_mistral': 3792, 'n_tokens_neox': 3343, 'n_words': 1198}