/*******************************************************************************
 * Copyright (c) 2000, 2012 QNX Software Systems 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
 *
 * Contributors:
 *     QNX Software Systems - Initial API and implementation
 *     Ken Ryall (Nokia) - bugs 118894, 170027, 91771
 *     Wind River Systems - adapted to work with platform Modules view (bug 210558)
 *     Marc-Andre Laperle - Bug 382462
 *******************************************************************************/
package org.eclipse.cdt.debug.internal.core.model;

import java.io.File;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.StringTokenizer;

import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.IAddress;
import org.eclipse.cdt.core.IAddressFactory;
import org.eclipse.cdt.core.IBinaryParser.IBinaryObject;
import org.eclipse.cdt.core.IBinaryParser.ISymbol;
import org.eclipse.cdt.debug.core.CDIDebugModel;
import org.eclipse.cdt.debug.core.CDebugCorePlugin;
import org.eclipse.cdt.debug.core.CDebugUtils;
import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants;
import org.eclipse.cdt.debug.core.ICDebugConstants;
import org.eclipse.cdt.debug.core.ICGlobalVariableManager;
import org.eclipse.cdt.debug.core.cdi.CDIException;
import org.eclipse.cdt.debug.core.cdi.ICDIAddressLocation;
import org.eclipse.cdt.debug.core.cdi.ICDIBreakpointHit;
import org.eclipse.cdt.debug.core.cdi.ICDIEndSteppingRange;
import org.eclipse.cdt.debug.core.cdi.ICDIErrorInfo;
import org.eclipse.cdt.debug.core.cdi.ICDIEventBreakpointHit;
import org.eclipse.cdt.debug.core.cdi.ICDIFunctionLocation;
import org.eclipse.cdt.debug.core.cdi.ICDILineLocation;
import org.eclipse.cdt.debug.core.cdi.ICDILocation;
import org.eclipse.cdt.debug.core.cdi.ICDISession;
import org.eclipse.cdt.debug.core.cdi.ICDISessionConfiguration;
import org.eclipse.cdt.debug.core.cdi.ICDISessionObject;
import org.eclipse.cdt.debug.core.cdi.ICDISharedLibraryEvent;
import org.eclipse.cdt.debug.core.cdi.ICDISignalReceived;
import org.eclipse.cdt.debug.core.cdi.ICDIWatchpointScope;
import org.eclipse.cdt.debug.core.cdi.ICDIWatchpointTrigger;
import org.eclipse.cdt.debug.core.cdi.event.ICDIChangedEvent;
import org.eclipse.cdt.debug.core.cdi.event.ICDICreatedEvent;
import org.eclipse.cdt.debug.core.cdi.event.ICDIDestroyedEvent;
import org.eclipse.cdt.debug.core.cdi.event.ICDIDisconnectedEvent;
import org.eclipse.cdt.debug.core.cdi.event.ICDIEvent;
import org.eclipse.cdt.debug.core.cdi.event.ICDIEventListener;
import org.eclipse.cdt.debug.core.cdi.event.ICDIExitedEvent;
import org.eclipse.cdt.debug.core.cdi.event.ICDIRestartedEvent;
import org.eclipse.cdt.debug.core.cdi.event.ICDIResumedEvent;
import org.eclipse.cdt.debug.core.cdi.event.ICDISuspendedEvent;
import org.eclipse.cdt.debug.core.cdi.model.ICDIAddressFactoryManagement;
import org.eclipse.cdt.debug.core.cdi.model.ICDIDisposable;
import org.eclipse.cdt.debug.core.cdi.model.ICDIGlobalVariableDescriptor;
import org.eclipse.cdt.debug.core.cdi.model.ICDIObject;
import org.eclipse.cdt.debug.core.cdi.model.ICDISharedLibrary;
import org.eclipse.cdt.debug.core.cdi.model.ICDISignal;
import org.eclipse.cdt.debug.core.cdi.model.ICDITarget;
import org.eclipse.cdt.debug.core.cdi.model.ICDITarget2;
import org.eclipse.cdt.debug.core.cdi.model.ICDITargetConfiguration;
import org.eclipse.cdt.debug.core.cdi.model.ICDITargetConfiguration2;
import org.eclipse.cdt.debug.core.cdi.model.ICDIThread;
import org.eclipse.cdt.debug.core.cdi.model.ICDIVariableDescriptor;
import org.eclipse.cdt.debug.core.model.CDebugElementState;
import org.eclipse.cdt.debug.core.model.ICBreakpoint;
import org.eclipse.cdt.debug.core.model.ICBreakpointType;
import org.eclipse.cdt.debug.core.model.ICDebugElement;
import org.eclipse.cdt.debug.core.model.ICDebugElementStatus;
import org.eclipse.cdt.debug.core.model.ICDebugTarget;
import org.eclipse.cdt.debug.core.model.ICGlobalVariable;
import org.eclipse.cdt.debug.core.model.ICLineBreakpoint;
import org.eclipse.cdt.debug.core.model.ICModule;
import org.eclipse.cdt.debug.core.model.ICSignal;
import org.eclipse.cdt.debug.core.model.IDebuggerProcessSupport;
import org.eclipse.cdt.debug.core.model.IDisassembly;
import org.eclipse.cdt.debug.core.model.IExecFileInfo;
import org.eclipse.cdt.debug.core.model.IGlobalVariableDescriptor;
import org.eclipse.cdt.debug.core.model.IModuleRetrieval;
import org.eclipse.cdt.debug.core.model.IPersistableRegisterGroup;
import org.eclipse.cdt.debug.core.model.IRegisterDescriptor;
import org.eclipse.cdt.debug.core.sourcelookup.CProjectSourceContainer;
import org.eclipse.cdt.debug.core.sourcelookup.ICSourceLocator;
import org.eclipse.cdt.debug.core.sourcelookup.ISourceLookupChangeListener;
import org.eclipse.cdt.debug.internal.core.CBreakpointManager;
import org.eclipse.cdt.debug.internal.core.CGlobalVariableManager;
import org.eclipse.cdt.debug.internal.core.CMemoryBlockRetrievalExtension;
import org.eclipse.cdt.debug.internal.core.CRegisterManager;
import org.eclipse.cdt.debug.internal.core.CSettingsManager;
import org.eclipse.cdt.debug.internal.core.CSignalManager;
import org.eclipse.cdt.debug.internal.core.ICDebugInternalConstants;
import org.eclipse.cdt.debug.internal.core.sourcelookup.CSourceLookupParticipant;
import org.eclipse.cdt.debug.internal.core.sourcelookup.CSourceManager;
import org.eclipse.cdt.utils.Addr64Factory;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IMarkerDelta;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Preferences;
import org.eclipse.core.runtime.Preferences.IPropertyChangeListener;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.DebugEvent;
import org.eclipse.debug.core.DebugException;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.IExpressionListener;
import org.eclipse.debug.core.IExpressionManager;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchListener;
import org.eclipse.debug.core.model.IBreakpoint;
import org.eclipse.debug.core.model.IDebugTarget;
import org.eclipse.debug.core.model.IExpression;
import org.eclipse.debug.core.model.IMemoryBlock;
import org.eclipse.debug.core.model.IMemoryBlockRetrieval;
import org.eclipse.debug.core.model.IMemoryBlockRetrievalExtension;
import org.eclipse.debug.core.model.IProcess;
import org.eclipse.debug.core.model.IRegisterGroup;
import org.eclipse.debug.core.model.ISourceLocator;
import org.eclipse.debug.core.model.IThread;
import org.eclipse.debug.core.sourcelookup.ISourceContainer;
import org.eclipse.debug.core.sourcelookup.ISourceLookupDirector;
import org.eclipse.debug.core.sourcelookup.ISourceLookupParticipant;
import org.eclipse.debug.core.sourcelookup.containers.DirectorySourceContainer;
import org.eclipse.debug.core.sourcelookup.containers.FolderSourceContainer;
import org.eclipse.debug.core.sourcelookup.containers.ProjectSourceContainer;

import com.ibm.icu.text.MessageFormat;

/**
 * Debug target for C/C++ debug model.
 */
public class CDebugTarget extends CDebugElement implements ICDebugTarget, ICDIEventListener, ILaunchListener, IExpressionListener, ISourceLookupChangeListener {

	/**
	 * Threads contained in this debug target. 
	 * When a thread starts it is added to the list. 
	 * When a thread ends it is removed from the list.
	 */
	private ArrayList<CThread> fThreads;

	/**
	 * Associated inferior process, or <code>null</code> if not available.
	 */
	private IProcess fDebuggeeProcess = null;

	/**
	 * The underlying CDI target.
	 */
	private ICDITarget fCDITarget;

	/**
	 * The name of this target.
	 */
	private String fName;

	/**
	 * The launch this target is contained in
	 */
	private ILaunch fLaunch;

	/**
	 * The debug configuration of this session
	 */
	private ICDITargetConfiguration fConfig;

    /**
     * The disassembly manager for this target.
     */
    private Disassembly fDisassembly;

    /**
     * The new disassembly manager for this target.
     */
    private DisassemblyRetrieval fDisassemblyRetrieval;

	/**
	 * The module manager for this target.
	 */
	private CModuleManager fModuleManager;

	/**
	 * The signal manager for this target.
	 */
	private CSignalManager fSignalManager;

	/**
	 * The register manager for this target.
	 */
	private CRegisterManager fRegisterManager;

	/**
	 * A breakpoint manager for this target.
	 */
	private CBreakpointManager fBreakpointManager;

	/**
	 * The global variable manager for this target.
	 */
	private CGlobalVariableManager fGlobalVariableManager;
	
	/**
	 * container for Default format information
	 */
	private CSettingsManager fFormatManager; 

	/**
	 * The executable binary file associated with this target.
	 */
	private IBinaryObject fBinaryFile;

	/** 
	 * The project associated with this target.
	 */
	private IProject fProject;
	
	/**
	 * Whether the target is little endian.
	 */
	private Boolean fIsLittleEndian = null;

	/**
	 * The target's preference set.
	 */
	private Preferences fPreferences = null;

	/**
	 * The address factory of this target.
	 */
	private IAddressFactory fAddressFactory;

	/**
	 * Support for the memory retrieval on this target.
	 */
	private CMemoryBlockRetrievalExtension fMemoryBlockRetrieval;

	/**
	 * Internal ID that uniquely identifies this CDebugTarget.
	 */
	private String internalD = Integer.toString(lastInternalID++);
	
	private static int lastInternalID = 1;

	/**
	 * Constructor for CDebugTarget.
	 */
	public CDebugTarget(ILaunch launch, IProject project, ICDITarget cdiTarget, String name, IProcess debuggeeProcess, IBinaryObject file, boolean allowsTerminate, boolean allowsDisconnect) {
		super(null);
		setLaunch(launch);
		setDebugTarget(this);
		setName(name);
		setProcess(debuggeeProcess);
		setProject(project);
		setExecFile(file);
		setCDITarget(cdiTarget);
		setState(CDebugElementState.SUSPENDED);
		initializePreferences();
		setConfiguration(cdiTarget.getConfiguration());
		setThreadList(new ArrayList<CThread>(5));
		createDisassembly();
		setModuleManager(new CModuleManager(this));
		setSignalManager(new CSignalManager(this));
		setRegisterManager(new CRegisterManager(this));
		setBreakpointManager(new CBreakpointManager(this));
		setGlobalVariableManager(new CGlobalVariableManager(this));
		setFormatManager(new CSettingsManager(this));
		setMemoryBlockRetrieval(new CMemoryBlockRetrievalExtension(this));
		initialize();
		DebugPlugin.getDefault().getLaunchManager().addLaunchListener(this);
		DebugPlugin.getDefault().getExpressionManager().addExpressionListener(this);
		getCDISession().getEventManager().addEventListener(this);
	}

	protected void initialize() {
		initializeSourceLookupPath();
		ArrayList<DebugEvent> debugEvents = new ArrayList<DebugEvent>(1);
		debugEvents.add(createCreateEvent());
		initializeThreads(debugEvents);
		initializeBreakpoints();
		initializeRegisters();
		initializeSourceManager();
		initializeModuleManager();
		initializeMemoryBlocks();
		getLaunch().addDebugTarget(this);
		fireEventSet(debugEvents.toArray(new DebugEvent[debugEvents.size()]));
	}

	private void initializeBreakpoints() {
		getBreakpointManager().initialize();
	}

	public void start(String stopSymbol, boolean resume) throws DebugException {
		ICDITargetConfiguration config = getConfiguration();
		if (config.supportsBreakpoints()) {
			getBreakpointManager().setInitialBreakpoints();

			if (stopSymbol != null && stopSymbol.length() != 0) {
				// See if the expression is a numeric address
				try {
					IAddress address = getAddressFactory().createAddress(stopSymbol);
					stopAtAddress(address);
				} catch (NumberFormatException nfexc) {
					// OK, expression is not a simple, absolute numeric value; keep trucking and try to resolve as expression
					stopAtSymbol(stopSymbol);	
				}
			}
		}
		if (config.supportsResume() && resume) {
			resume();
		}
	}

	/**
	 * Adds all of the pre-existing threads to this debug target.
	 */
	protected void initializeThreads(List<DebugEvent> debugEvents) {
		final ICDITarget cdiTarget = getCDITarget();
		if (cdiTarget == null) {
			return;
		}
		
		ICDIThread[] cdiThreads = new ICDIThread[0];
		try {
			cdiThreads = cdiTarget.getThreads();
		} catch (CDIException e) {
			// ignore
		}
		DebugEvent suspendEvent = null;
		for (int i = 0; i < cdiThreads.length; ++i) {
			CThread thread = createThread(cdiThreads[i]);
			debugEvents.add(thread.createCreateEvent());
			try {
				if (cdiThreads[i].equals(cdiTarget.getCurrentThread()) && thread.isSuspended()) {
					// Use BREAKPOINT as a detail to force perspective switch
					suspendEvent = thread.createSuspendEvent(DebugEvent.BREAKPOINT);
				}
			} catch (CDIException e) {
				// ignore
			}
		}
		if (suspendEvent != null) {
			debugEvents.add(suspendEvent);
		}
	}

	protected void initializeRegisters() {
		getRegisterManager().initialize();
	}

	protected void initializeSourceManager() {
		ISourceLocator locator = getLaunch().getSourceLocator();
		if (locator instanceof IAdaptable) {
			ICSourceLocator clocator = (ICSourceLocator)((IAdaptable)locator).getAdapter(ICSourceLocator.class);
			if (clocator instanceof IAdaptable) {
				CSourceManager sm = (CSourceManager)((IAdaptable)clocator).getAdapter(CSourceManager.class);
				if (sm != null)
					sm.setDebugTarget(this);
			}
			IResourceChangeListener listener = (IResourceChangeListener)((IAdaptable)locator).getAdapter(IResourceChangeListener.class);
			if (listener != null)
				CCorePlugin.getWorkspace().addResourceChangeListener(listener);
		}
	}

	protected void initializeSourceLookupPath() {
		ISourceLocator locator = getLaunch().getSourceLocator();
		if (locator instanceof ISourceLookupDirector) {
			ISourceLookupParticipant[] participants = ((ISourceLookupDirector)locator).getParticipants();
			for (int i = 0; i < participants.length; ++i) {
				if (participants[i] instanceof CSourceLookupParticipant) {
					((CSourceLookupParticipant)participants[i]).addSourceLookupChangeListener(this);
				}
			}
			setSourceLookupPath(((ISourceLookupDirector)locator).getSourceContainers());
		}
	}

	protected void initializeModuleManager() {
		final ICDITarget cdiTarget = getCDITarget();
		if (cdiTarget == null) {
			return;
		}
		
		ICDISharedLibrary[] slibs = new ICDISharedLibrary[0];
		try {
			slibs = cdiTarget.getSharedLibraries();
		} catch (CDIException e) {
			DebugPlugin.log(e);
		}
		ICModule[] modules = null;
		if (getExecFile() != null) {
			modules = new ICModule[slibs.length + 1];
			modules[0] = CModule.createExecutable(this, getExecFile().getPath());
		} else {
			modules = new ICModule[slibs.length];
		}
		for (int i = 0; i < slibs.length; ++i) {
			modules[i + 1] = CModule.createSharedLibrary(this, slibs[i]);
		}
		getModuleManager().addModules(modules);
	}

	protected void initializeMemoryBlocks() {
		getMemoryBlockRetrieval().initialize();
	}

	/* (non-Javadoc)
	 * @see org.eclipse.debug.core.model.IDebugTarget#getProcess()
	 */
	@Override
	public IProcess getProcess() {
		return fDebuggeeProcess;
	}

	/**
	 * Sets the process associated with this debug target, possibly <code>null</code>. Set on creation.
	 * 
	 * @param process the system process associated with the underlying CDI target, 
	 * or <code>null</code> if no process is associated with this debug target 
	 * (for a core dump debugging).
	 */
	protected void setProcess(IProcess debuggeeProcess) {
		fDebuggeeProcess = debuggeeProcess;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.debug.core.model.IDebugTarget#getThreads()
	 */
	@Override
	public IThread[] getThreads() {
		List<CThread> threads = getThreadList();
		return threads.toArray(new IThread[threads.size()]);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.debug.core.model.IDebugTarget#hasThreads()
	 */
	@Override
	public boolean hasThreads() throws DebugException {
		return getThreadList().size() > 0;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.debug.core.model.IDebugTarget#getName()
	 */
	@Override
	public String getName() throws DebugException {
		return fName;
	}

	/**
	 * Sets the name of this debug target.
	 * 
	 * @param name the name of this debug target
	 */
	public void setName(String name) {
		fName = name;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.debug.core.model.IDebugTarget#supportsBreakpoint(org.eclipse.debug.core.model.IBreakpoint)
	 */
	@Override
	public boolean supportsBreakpoint(IBreakpoint breakpoint) {
		if (!getConfiguration().supportsBreakpoints())
			return false;
		return (breakpoint instanceof ICBreakpoint && getBreakpointManager().supportsBreakpoint((ICBreakpoint)breakpoint));
	}

	/* (non-Javadoc)
	 * @see org.eclipse.debug.core.ILaunchListener#launchRemoved(org.eclipse.debug.core.ILaunch)
	 */
	@Override
	public void launchRemoved(ILaunch launch) {
		if (!isAvailable()) {
			return;
		}
		if (launch.equals(getLaunch())) {
			// This target has been deregistered, but it hasn't been successfully terminated.
			// Update internal state to reflect that it is disconnected
			disconnected();
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.debug.core.ILaunchListener#launchAdded(org.eclipse.debug.core.ILaunch)
	 */
	@Override
	public void launchAdded(ILaunch launch) {
	}

	/* (non-Javadoc)
	 * @see org.eclipse.debug.core.ILaunchListener#launchChanged(org.eclipse.debug.core.ILaunch)
	 */
	@Override
	public void launchChanged(ILaunch launch) {
	}

	/* (non-Javadoc)
	 * @see org.eclipse.debug.core.model.ITerminate#canTerminate()
	 */
	@Override
	public boolean canTerminate() {
		return supportsTerminate() && isAvailable();
	}

	/* (non-Javadoc)
	 * @see org.eclipse.debug.core.model.ITerminate#isTerminated()
	 */
	@Override
	public boolean isTerminated() {
		return (getState().equals(CDebugElementState.TERMINATED));
	}

	/* (non-Javadoc)
	 * @see org.eclipse.debug.core.model.ITerminate#terminate()
	 */
	@Override
	public void terminate() throws DebugException {
		if (!canTerminate()) {
			return;
		}
		final CDebugElementState newState = CDebugElementState.TERMINATING;		
		changeState(newState);
		try {
			final ICDITarget cdiTarget = getCDITarget();
			if (cdiTarget != null) {
				cdiTarget.terminate();
			}
		} catch (CDIException e) {
			if (getState() == newState) {
				restoreOldState();
			}
			targetRequestFailed(e.getMessage(), null);
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.debug.core.model.ISuspendResume#canResume()
	 */
	@Override
	public boolean canResume() {
		return getConfiguration().supportsResume() && isSuspended();
	}

	/* (non-Javadoc)
	 * @see org.eclipse.debug.core.model.ISuspendResume#canSuspend()
	 */
	@Override
	public boolean canSuspend() {
		if (!getConfiguration().supportsSuspend())
			return false;
		if (getState().equals(CDebugElementState.RESUMED)) {
			// only allow suspend if no threads are currently suspended
			IThread[] threads = getThreads();
			for (int i = 0; i < threads.length; i++) {
				if (threads[i].isSuspended()) {
					return false;
				}
			}
			return true;
		}
		return false;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.debug.core.model.ISuspendResume#isSuspended()
	 */
	@Override
	public boolean isSuspended() {
		return (getState().equals(CDebugElementState.SUSPENDED));
	}

	/* (non-Javadoc)
	 * @see org.eclipse.debug.core.model.ISuspendResume#resume()
	 */
	@Override
	public void resume() throws DebugException {
		if (!canResume())
			return;
		final CDebugElementState newState = CDebugElementState.RESUMING; 
		changeState(newState);
		try {
			final ICDITarget cdiTarget = getCDITarget();
			if (cdiTarget != null) {
				cdiTarget.resume(false);
			}
		} catch (CDIException e) {
			if (getState() == newState) {
				restoreOldState();
			}
			targetRequestFailed(e.getMessage(), null);
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.debug.core.model.ISuspendResume#suspend()
	 */
	@Override
	public void suspend() throws DebugException {
		if (!canSuspend())
			return;
		final CDebugElementState newState = CDebugElementState.SUSPENDING; 
		changeState(newState);
		try {
			final ICDITarget cdiTarget = getCDITarget();
			if (cdiTarget != null) {
				cdiTarget.suspend();
			}
		} catch (CDIException e) {
			if (getState() == newState) {
				restoreOldState();
			}
			targetRequestFailed(e.getMessage(), null);
		}
	}

	protected boolean isSuspending() {
		return (getState().equals(CDebugElementState.SUSPENDING));
	}

	/**
	 * Notifies threads that the target has been suspended. 
	 */
	protected void suspendThreads(ICDISuspendedEvent event) {
		Iterator<CThread> it = getThreadList().iterator();
		while (it.hasNext()) {
			CThread thread = it.next();
			ICDIThread suspensionThread = null;
			try {
				final ICDITarget cdiTarget = getCDITarget();
				if (cdiTarget != null) {
					suspensionThread = cdiTarget.getCurrentThread();
				}
			} catch (CDIException e) {
				// ignore
			}
			thread.suspendByTarget(event.getReason(), suspensionThread);
		}
	}

	/**
	 * Refreshes the thread list.
	 */
	protected synchronized List<CThread> refreshThreads() {
		ArrayList<CThread> newThreads = new ArrayList<CThread>(5);
		ArrayList<CThread> list = new ArrayList<CThread>(5);
		ArrayList<DebugEvent> debugEvents = new ArrayList<DebugEvent>(5);
		List<CThread> oldList = (List<CThread>)getThreadList().clone();
		ICDIThread[] cdiThreads = new ICDIThread[0];
		ICDIThread currentCDIThread = null;
		try {
			final ICDITarget cdiTarget = getCDITarget();
			if (cdiTarget != null) {
				cdiThreads = cdiTarget.getThreads();
				currentCDIThread = cdiTarget.getCurrentThread();
			}
		} catch (CDIException e) {
		}
		for (int i = 0; i < cdiThreads.length; ++i) {
			CThread thread = findThread(oldList, cdiThreads[i]);
			if (thread == null) {
				thread = new CThread(this, cdiThreads[i]);
				newThreads.add(thread);
			}
			else {
				oldList.remove(thread);
			}
			thread.setCurrent(cdiThreads[i].equals(currentCDIThread));
			list.add(thread);
		}
		Iterator<CThread> it = oldList.iterator();
		while (it.hasNext()) {
			CThread thread = it.next();
			thread.terminated();
			debugEvents.add(thread.createTerminateEvent());
		}
		setThreadList(list);
		it = newThreads.iterator();
		while (it.hasNext()) {
			debugEvents.add(it.next().createCreateEvent());
		}
		if (debugEvents.size() > 0)
			fireEventSet(debugEvents.toArray(new DebugEvent[debugEvents.size()]));
		return newThreads;
	}

	/**
	 * Notifies threads that the target has been resumed.
	 */
	protected synchronized void resumeThreads(List<DebugEvent> debugEvents, int detail) {
		Iterator<CThread> it = getThreadList().iterator();
		while (it.hasNext()) {
			it.next().resumedByTarget(detail, debugEvents);
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.debug.core.IBreakpointListener#breakpointAdded(org.eclipse.debug.core.model.IBreakpoint)
	 */
	@Override
	public void breakpointAdded(IBreakpoint breakpoint) {
	}

	/* (non-Javadoc)
	 * @see org.eclipse.debug.core.IBreakpointListener#breakpointRemoved(org.eclipse.debug.core.model.IBreakpoint, org.eclipse.core.resources.IMarkerDelta)
	 */
	@Override
	public void breakpointRemoved(IBreakpoint breakpoint, IMarkerDelta delta) {
	}

	/* (non-Javadoc)
	 * @see org.eclipse.debug.core.IBreakpointListener#breakpointChanged(org.eclipse.debug.core.model.IBreakpoint, org.eclipse.core.resources.IMarkerDelta)
	 */
	@Override
	public void breakpointChanged(IBreakpoint breakpoint, IMarkerDelta delta) {
	}

	/**
	 * Returns whether this debug target supports disconnecting.
	 * 
	 * @return whether this debug target supports disconnecting
	 */
	protected boolean supportsDisconnect() {
		return getConfiguration().supportsDisconnect();
	}

	/**
	 * Returns whether this debug target supports termination.
	 * 
	 * @return whether this debug target supports termination
	 */
	protected boolean supportsTerminate() {
		return getConfiguration().supportsTerminate();
	}

	/* (non-Javadoc)
	 * @see org.eclipse.debug.core.model.IDisconnect#canDisconnect()
	 */
	@Override
	public boolean canDisconnect() {
		return supportsDisconnect() && isAvailable();
	}

	/* (non-Javadoc)
	 * @see org.eclipse.debug.core.model.IDisconnect#disconnect()
	 */
	@Override
	public void disconnect() throws DebugException {
		if (isDisconnecting()) {
			return;
		}
		final CDebugElementState newState = CDebugElementState.DISCONNECTING;		
		changeState(newState);
		try {
			final ICDITarget cdiTarget = getCDITarget();
			if (cdiTarget != null) {
				cdiTarget.disconnect();
			}
		} catch (CDIException e) {
			if (getState() == newState) {
				restoreOldState();
			}
			targetRequestFailed(e.getMessage(), null);
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.debug.core.model.IDisconnect#isDisconnected()
	 */
	@Override
	public boolean isDisconnected() {
		return (getState().equals(CDebugElementState.DISCONNECTED));
	}

	/* (non-Javadoc)
	 * @see org.eclipse.debug.core.model.IMemoryBlockRetrieval#supportsStorageRetrieval()
	 */
	@Override
	public boolean supportsStorageRetrieval() {
		return false;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.debug.core.model.IMemoryBlockRetrieval#getMemoryBlock(long, long)
	 */
	@Override
	public IMemoryBlock getMemoryBlock(long startAddress, long length) throws DebugException {
		return null;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.debug.core.model.IDebugElement#getLaunch()
	 */
	@Override
    public ILaunch getLaunch() {
		return fLaunch;
	}

	/**
	 * Sets the launch this target is contained in
	 * 
	 * @param launch the launch this target is contained in
	 */
	private void setLaunch(ILaunch launch) {
		fLaunch = launch;
	}

	/**
	 * Returns the list of threads contained in this debug target.
	 * 
	 * @return list of threads
	 */
	protected ArrayList<CThread> getThreadList() {
		return fThreads;
	}

	/**
	 * Sets the list of threads contained in this debug target. Set to an empty collection on creation. Threads are added and removed as they start and end. On
	 * termination this collection is set to the immutable singleton empty list.
	 * 
	 * @param threads empty list
	 */
	private void setThreadList(ArrayList<CThread> threads) {
		fThreads = threads;
	}

	private void setCDITarget(ICDITarget cdiTarget) {
		fCDITarget = cdiTarget;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
	 */
	@Override
    public Object getAdapter(@SuppressWarnings("rawtypes") Class adapter) {
		if (adapter.equals(ICDebugElement.class))
			return this;
		if (adapter.equals(CDebugElement.class))
			return this;
		if (adapter.equals(IDebugTarget.class))
			return this;
		if (adapter.equals(ICDebugTarget.class))
			return this;
		if (adapter.equals(CDebugTarget.class))
			return this;
		if (adapter.equals(ICDITarget.class))
			return fCDITarget;
		if (adapter.equals(IDebuggerProcessSupport.class))
			return this;
		if (adapter.equals(IExecFileInfo.class))
			return this;
		if (adapter.equals(CBreakpointManager.class))
			return getBreakpointManager();
		if (adapter.equals(CSignalManager.class))
			return getSignalManager();
		if (adapter.equals(CRegisterManager.class))
			return getRegisterManager();
		if (adapter.equals(ICGlobalVariableManager.class))
			return getGlobalVariableManager();
		if (adapter.equals(ICDISession.class))
			return getCDISession();
		if (adapter.equals(IMemoryBlockRetrievalExtension.class))
			return getMemoryBlockRetrieval();
		if (adapter.equals(IMemoryBlockRetrieval.class))
			return getMemoryBlockRetrieval();
		if (adapter.equals(IModuleRetrieval.class))
			return getModuleManager();
		
        // Force adapters to be loaded.  Otherwise the adapter manager may not find
        // the model proxy adapter for CDT debug elements.
        Platform.getAdapterManager().loadAdapter(this, adapter.getName());

		return super.getAdapter(adapter);
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.debug.core.cdi.event.ICDIEventListener#handleDebugEvents(org.eclipse.cdt.debug.core.cdi.event.ICDIEvent[])
	 */
	@Override
	public void handleDebugEvents(ICDIEvent[] events) {
		for (int i = 0; i < events.length; i++) {
			ICDIEvent event = events[i];
			ICDIObject source = event.getSource();
			final ICDITarget cdiTarget = getCDITarget();
			if (source == null && event instanceof ICDIDestroyedEvent) {
				handleTerminatedEvent((ICDIDestroyedEvent)event);
			}
			else if (source != null && cdiTarget != null && source.getTarget().equals(cdiTarget)) {
				if (event instanceof ICDICreatedEvent) {
					if (source instanceof ICDIThread) {
						handleThreadCreatedEvent((ICDICreatedEvent)event);
					}
					if (source instanceof ICDISharedLibrary) {
						getModuleManager().sharedLibraryLoaded((ICDISharedLibrary)source);
					}
				}
				else if (event instanceof ICDISuspendedEvent) {
					if (source instanceof ICDITarget) {
						handleSuspendedEvent((ICDISuspendedEvent)event);
					}
				}
				else if (event instanceof ICDIResumedEvent) {
					if (source instanceof ICDITarget) {
						handleResumedEvent((ICDIResumedEvent)event);
					}
				}
				else if (event instanceof ICDIExitedEvent) {
					if (source instanceof ICDITarget) {
						handleExitedEvent((ICDIExitedEvent)event);
					}
				}
				else if (event instanceof ICDIDestroyedEvent) {
					if (source instanceof ICDIThread) {
						handleThreadTerminatedEvent((ICDIDestroyedEvent)event);
					}
					if (source instanceof ICDISharedLibrary) {
						getModuleManager().sharedLibraryUnloaded((ICDISharedLibrary)source);
					}
				}
				else if (event instanceof ICDIDisconnectedEvent) {
					if (source instanceof ICDITarget) {
						handleDisconnectedEvent((ICDIDisconnectedEvent)event);
					}
				}
				else if (event instanceof ICDIChangedEvent) {
					if (source instanceof ICDITarget) {
						handleChangedEvent((ICDIChangedEvent)event);
					}
					if (source instanceof ICDISharedLibrary) {
						handleSymbolsLoaded((ICDISharedLibrary)source);
					}
					if (source instanceof ICDISignal) {
						getSignalManager().signalChanged((ICDISignal)source);
					}
				}
				else if (event instanceof ICDIRestartedEvent) {
					if (source instanceof ICDITarget) {
						handleRestartedEvent((ICDIRestartedEvent)event);
					}
				}
			}
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.debug.core.model.IRestart#canRestart()
	 */
	@Override
	public boolean canRestart() {
		return getConfiguration().supportsRestart() && isSuspended();
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.debug.core.model.IRestart#restart()
	 */
	@Override
	public void restart() throws DebugException {
		if (!canRestart()) {
			return;
		}
		final ICDITarget cdiTarget = getCDITarget();
		if (cdiTarget == null) {
			return;
		}
		
		try {
			ILaunchConfiguration launchConfig = getLaunch().getLaunchConfiguration();
			if (launchConfig.getAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN, ICDTLaunchConfigurationConstants.DEBUGGER_STOP_AT_MAIN_DEFAULT)) {
				String mainSymbol = launchConfig.getAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN_SYMBOL, ICDTLaunchConfigurationConstants.DEBUGGER_STOP_AT_MAIN_SYMBOL_DEFAULT);
				ICDILocation location = null;
				// See if the expression is a numeric address
				try {
					IAddress address = getAddressFactory().createAddress(mainSymbol);
					location = cdiTarget.createAddressLocation(address.getValue());
				} catch (NumberFormatException nfexc) {
					// OK, expression is not a simple, absolute numeric value; keep trucking and try to resolve as expression
					location = cdiTarget.createFunctionLocation("", mainSymbol); //$NON-NLS-1$	
				}
				
				setInternalTemporaryBreakpoint(location);
			}
		} catch (CoreException e) {
			requestFailed(e.getMessage(), e);
		}

		final CDebugElementState newState = CDebugElementState.RESTARTING;
		changeState(newState);
		try {
			cdiTarget.restart();
		} catch (CDIException e) {
			if (getState() == newState) {
				restoreOldState();
			}
			targetRequestFailed(e.getMessage(), e);
		}
	}

	/**
	 * Returns whether this target is available to handle client requests.
	 * 
	 * @return whether this target is available to handle client requests
	 */
	public boolean isAvailable() {
		return !(isTerminated() || isTerminating() || isDisconnected() || isDisconnecting());
	}

	/**
	 * Returns whether this target is in the process of terminating.
	 * 
	 * @return whether this target is terminating
	 */
	protected boolean isTerminating() {
		return (getState().equals(CDebugElementState.TERMINATING));
	}

	/**
	 * Updates the state of this target to be terminated, if not already terminated.
	 */
	protected void terminated() {
		if (!isTerminated()) {
			if (!isDisconnected()) {
				setState(CDebugElementState.TERMINATED);
			}
			cleanup();
			fireTerminateEvent();
		}
	}

	/**
	 * Returns whether this target is in the process of terminating.
	 * 
	 * @return whether this target is terminating
	 */
	protected boolean isDisconnecting() {
		return (getState().equals(CDebugElementState.DISCONNECTING));
	}

	/**
	 * Updates the state of this target for disconnection.
	 */
	protected void disconnected() {
		if (!isDisconnected()) {
			setState(CDebugElementState.DISCONNECTED);
			cleanup();
			fireTerminateEvent();
		}
	}

	/**
	 * Cleans up the internal state of this debug target as a result of a session ending.
	 */
	protected void cleanup() {
		resetStatus();
		removeAllThreads();
		getCDISession().getEventManager().removeEventListener(this);
		DebugPlugin.getDefault().getExpressionManager().removeExpressionListener(this);
		DebugPlugin.getDefault().getLaunchManager().removeLaunchListener(this);
		saveFormats();
		saveGlobalVariables();
		disposeGlobalVariableManager();
		disposeModuleManager();
		disposeSignalManager();
		saveRegisterGroups();
		disposeRegisterManager();
		saveMemoryBlocks();
		disposeMemoryBlockRetrieval();
		disposeDisassembly();
		disposeSourceManager();
		disposeSourceLookupPath();
		disposeBreakpointManager();
		removeAllExpressions();
		disposePreferences();
		
		ICDITarget cdiTarget = getCDITarget();
		// TODO: apparently we're not really done with the ICDITarget. The
		// arrival of a terminate event from gdb requires access to this (see
		// SessionManager.handleDebugEvent()). Reported by Mikhail. Need to
		// revisit this.
		// setCDITarget(null); 
		if (cdiTarget instanceof ICDIDisposable) {
			((ICDIDisposable)cdiTarget).dispose();
		}		
	}

	/**
	 * Removes all threads from this target's collection of threads, firing a terminate event for each.
	 */
	protected void removeAllThreads() {
		List<CThread> threads = getThreadList();
		setThreadList(new ArrayList<CThread>(0));
		ArrayList<DebugEvent> debugEvents = new ArrayList<DebugEvent>(threads.size());
		Iterator<CThread> it = threads.iterator();
		while (it.hasNext()) {
			CThread thread = it.next();
			thread.terminated();
			debugEvents.add(thread.createTerminateEvent());
		}
		fireEventSet(debugEvents.toArray(new DebugEvent[debugEvents.size()]));
	}

	/**
	 * Removes all expressions from this target.
	 */
	protected void removeAllExpressions() {
		IExpressionManager em = DebugPlugin.getDefault().getExpressionManager();
		IExpression[] expressions = em.getExpressions();
		for (int i = 0; i < expressions.length; ++i) {
			if (expressions[i] instanceof CExpression && expressions[i].getDebugTarget().equals(this)) {
				em.removeExpression(expressions[i]);
			}
		}
	}

	/**
	 * Creates, adds and returns a thread for the given underlying CDI thread. A creation event is fired for the thread. Returns <code>null</code> if during
	 * the creation of the thread this target is set to the disconnected state.
	 * 
	 * @param thread the underlying CDI thread
	 * @return model thread
	 */
	protected CThread createThread(ICDIThread cdiThread) {
		CThread thread = new CThread(this, cdiThread);
		getThreadList().add(thread);
		return thread;
	}

	private void handleSuspendedEvent(ICDISuspendedEvent event) {
		setState(CDebugElementState.SUSPENDED);
		ICDISessionObject reason = event.getReason();
		setCurrentStateInfo(reason);
		// Reset the registers that have errors.
		getRegisterManager().targetSuspended();
		getBreakpointManager().skipBreakpoints(false);
		List<CThread> newThreads = refreshThreads();
		if (event.getSource() instanceof ICDITarget) {
			if (!(this.getConfiguration() instanceof ICDITargetConfiguration2) || !((ICDITargetConfiguration2)this.getConfiguration()).supportsThreadControl())
				suspendThreads(event);
		} else if (event.getSource() instanceof ICDIThread) {
			// We need this for debuggers that don't have notifications
			// for newly created threads.
			CThread thread = findThread((ICDIThread)event.getSource());
			if (thread != null && newThreads.contains(thread)) {
				ICDIEvent[] evts = new ICDIEvent[]{ event };
				thread.handleDebugEvents(evts);
			}
		}
		if (reason instanceof ICDIEndSteppingRange) {
			handleEndSteppingRange((ICDIEndSteppingRange)reason);
		}
		else if (reason instanceof ICDIBreakpointHit) {
			handleBreakpointHit((ICDIBreakpointHit)reason);
		}
		else if (reason instanceof ICDISignalReceived) {
			handleSuspendedBySignal((ICDISignalReceived)reason);
		}
		else if (reason instanceof ICDIWatchpointTrigger) {
			handleWatchpointTrigger((ICDIWatchpointTrigger)reason);
		}
		else if (reason instanceof ICDIWatchpointScope) {
			handleWatchpointScope((ICDIWatchpointScope)reason);
		}
		else if (reason instanceof ICDIErrorInfo) {
			handleErrorInfo((ICDIErrorInfo)reason);
		}
		else if (reason instanceof ICDISharedLibraryEvent) {
			handleSuspendedBySolibEvent((ICDISharedLibraryEvent)reason);
		}
		else if (reason instanceof ICDIEventBreakpointHit) {
			handleEventBreakpointHit((ICDIEventBreakpointHit)reason);
		}
		else { // reason is not specified
			fireSuspendEvent(DebugEvent.UNSPECIFIED);
		}
	}

	private void handleResumedEvent(ICDIResumedEvent event) {
		setState(CDebugElementState.RESUMED);
		setCurrentStateInfo(null);
		resetStatus();
		ArrayList<DebugEvent> debugEvents = new ArrayList<DebugEvent>(10);
		int detail = DebugEvent.UNSPECIFIED;
		switch(event.getType()) {
			case ICDIResumedEvent.CONTINUE:
				detail = DebugEvent.CLIENT_REQUEST;
				break;
			case ICDIResumedEvent.STEP_INTO:
			case ICDIResumedEvent.STEP_INTO_INSTRUCTION:
				detail = DebugEvent.STEP_INTO;
				break;
			case ICDIResumedEvent.STEP_OVER:
			case ICDIResumedEvent.STEP_OVER_INSTRUCTION:
				detail = DebugEvent.STEP_OVER;
				break;
			case ICDIResumedEvent.STEP_RETURN:
				detail = DebugEvent.STEP_RETURN;
				break;
		}
		debugEvents.add(createResumeEvent(detail));
		
		if (!(this.getConfiguration() instanceof ICDITargetConfiguration2) || !((ICDITargetConfiguration2)this.getConfiguration()).supportsThreadControl())
			resumeThreads(debugEvents, detail);
		
		fireEventSet(debugEvents.toArray(new DebugEvent[debugEvents.size()]));
	}

	private void handleEndSteppingRange(ICDIEndSteppingRange endSteppingRange) {
		fireSuspendEvent(DebugEvent.UNSPECIFIED);
	}

	private void handleBreakpointHit(ICDIBreakpointHit breakpointHit) {
		fireSuspendEvent(DebugEvent.BREAKPOINT);
	}

	private void handleEventBreakpointHit(ICDIEventBreakpointHit breakpointHit) {
		fireSuspendEvent(DebugEvent.BREAKPOINT);
	}

	private void handleWatchpointTrigger(ICDIWatchpointTrigger wt) {
		fireSuspendEvent(DebugEvent.BREAKPOINT);
	}

	private void handleWatchpointScope(ICDIWatchpointScope ws) {
		getBreakpointManager().watchpointOutOfScope(ws.getWatchpoint());
		fireSuspendEvent(DebugEvent.BREAKPOINT);
	}

	private void handleSuspendedBySignal(ICDISignalReceived signal) {
		fireSuspendEvent(DebugEvent.CLIENT_REQUEST);
	}

	private void handleErrorInfo(ICDIErrorInfo info) {
		setStatus(ICDebugElementStatus.ERROR, (info != null) ? info.getMessage() : null);
		if (info != null) {
			MultiStatus status = new MultiStatus(CDebugCorePlugin.getUniqueIdentifier(), ICDebugInternalConstants.STATUS_CODE_ERROR, CoreModelMessages.getString("CDebugTarget.1"), //$NON-NLS-1$
					null);
			StringTokenizer st = new StringTokenizer(info.getDetailMessage(), "\n\r"); //$NON-NLS-1$
			while (st.hasMoreTokens()) {
				String token = st.nextToken();
				if (token.length() > 200) {
					token = token.substring(0, 200);
				}
				status.add(new Status(IStatus.ERROR, status.getPlugin(), ICDebugInternalConstants.STATUS_CODE_ERROR, token, null));
			}
			CDebugUtils.error(status, this);
		}
		fireSuspendEvent(DebugEvent.UNSPECIFIED);
	}

	private void handleSuspendedBySolibEvent(ICDISharedLibraryEvent solibEvent) {
		fireSuspendEvent(DebugEvent.UNSPECIFIED);
	}

	private void handleExitedEvent(ICDIExitedEvent event) {
		removeAllThreads();
		setState(CDebugElementState.EXITED);
		setCurrentStateInfo(event.getReason());
		fireChangeEvent(DebugEvent.CONTENT);
		ICDISessionConfiguration sessionConfig = getCDISession().getConfiguration();
		if (sessionConfig != null && sessionConfig.terminateSessionOnExit())
			terminated();
	}

	private void handleTerminatedEvent(ICDIDestroyedEvent event) {
		terminated();
	}

	private void handleDisconnectedEvent(ICDIDisconnectedEvent event) {
		disconnected();
	}

	private void handleChangedEvent(ICDIChangedEvent event) {
	}

	private void handleRestartedEvent(ICDIRestartedEvent event) {
	}

	private void handleThreadCreatedEvent(ICDICreatedEvent event) {
		ICDIThread cdiThread = (ICDIThread)event.getSource();
		CThread thread = findThread(cdiThread);
		if (thread == null) {
			thread = createThread(cdiThread);
			thread.fireCreationEvent();
		}
	}

	private void handleThreadTerminatedEvent(ICDIDestroyedEvent event) {
		ICDIThread cdiThread = (ICDIThread)event.getSource();
        List<CThread> threads = getThreadList();
        List<CThread> threadsToRemove = new ArrayList<CThread>(1);
        for (int i = 0; i < threads.size(); i++) {
        	CThread cthread = threads.get(i);
        	// It's possible CThread has handled the thread-terminated event
			// before us (by appearing first in the EventManager)
			// and has disassociated itself from the ICDIThread.
			// So handle any disassociated CThreads we find. Chances are
			// there's only one and it's the one we got the terminated event
			// for. See bugzilla 254888.  
        	ICDIThread cdithread = cthread.getCDIThread(); 
        	if (cdithread == null || cdithread.equals(cdiThread)) {
        		threadsToRemove.add(cthread);
        	}
        }
        for (CThread cthread : threadsToRemove) {
        	threads.remove(cthread);
        	cthread.terminated();
        	cthread.fireTerminateEvent();
        }
	}

	/**
	 * Finds and returns the model thread for the associated CDI thread, or <code>null</code> if not found.
	 * 
	 * @param the underlying CDI thread
	 * @return the associated model thread
	 */
	public CThread findThread(ICDIThread cdiThread) {
		return findThread(getThreadList(), cdiThread);
	}

	public CThread findThread(List<CThread> threads, ICDIThread cdiThread) {
		for (int i = 0; i < threads.size(); i++) {
			CThread t = threads.get(i);
			ICDIThread thisCdiThread = t.getCDIThread();
			if (thisCdiThread != null && thisCdiThread.equals(cdiThread))
				return t;
		}
		return null;
	}

	/**
	 * Returns the debug configuration of this target.
	 * 
	 * @return the debug configuration of this target
	 */
	protected ICDITargetConfiguration getConfiguration() {
		return fConfig;
	}

	/**
	 * Sets the debug configuration of this target.
	 * 
	 * @param config the debug configuration to set
	 */
	private void setConfiguration(ICDITargetConfiguration config) {
		fConfig = config;
	}

	protected boolean supportsExpressionEvaluation() {
		return getConfiguration().supportsExpressionEvaluation();
	}

	/* (non-Javadoc)
	 * @see org.eclipse.debug.core.IExpressionListener#expressionAdded(org.eclipse.debug.core.model.IExpression)
	 */
	@Override
	public void expressionAdded(IExpression expression) {
	}

	/* (non-Javadoc)
	 * @see org.eclipse.debug.core.IExpressionListener#expressionChanged(org.eclipse.debug.core.model.IExpression)
	 */
	@Override
	public void expressionChanged(IExpression expression) {
	}

	/* (non-Javadoc)
	 * @see org.eclipse.debug.core.IExpressionListener#expressionRemoved(org.eclipse.debug.core.model.IExpression)
	 */
	@Override
	public void expressionRemoved(IExpression expression) {
		if (expression instanceof CExpression && expression.getDebugTarget().equals(this)) {
			((CExpression)expression).dispose();
		}
	}

	public void setInternalTemporaryBreakpoint(ICDILocation location) throws DebugException {
		try {
			final ICDITarget cdiTarget = getCDITarget();
			if (cdiTarget == null) {
				return;
			}
			if (location instanceof ICDIFunctionLocation) {
				cdiTarget.setFunctionBreakpoint(ICBreakpointType.TEMPORARY, (ICDIFunctionLocation)location, null, false);
			} else if (location instanceof ICDILineLocation) {
				cdiTarget.setLineBreakpoint(ICBreakpointType.TEMPORARY, (ICDILineLocation)location, null, false);
			} else if (location instanceof ICDIAddressLocation) {
				cdiTarget.setAddressBreakpoint(ICBreakpointType.TEMPORARY, (ICDIAddressLocation)location, null, false);
			} else {
				// ???
				targetRequestFailed("not_a_location", null); //$NON-NLS-1$
			}
		} catch (CDIException e) {
			targetRequestFailed(e.getMessage(), null);
		}
	}

	protected IThread getCurrentThread() {
		IThread[] threads = getThreads();
		for (int i = 0; i < threads.length; ++i) {
			if (((CThread)threads[i]).isCurrent())
				return threads[i];
		}
		return null;
	}

	protected ISourceLocator getSourceLocator() {
		return getLaunch().getSourceLocator();
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.debug.core.model.IExecFileInfo#isLittleEndian()
	 */
	@Override
	public boolean isLittleEndian() {
		if (fIsLittleEndian == null) {
			fIsLittleEndian = Boolean.TRUE;
			IBinaryObject file;
			file = getBinaryFile();
			if (file != null) {
				fIsLittleEndian = Boolean.valueOf(file.isLittleEndian());
			}
		}
		return fIsLittleEndian.booleanValue();
	}
	
	/* (non-Javadoc)
	 * @see org.eclipse.cdt.debug.core.model.IExecFileInfo#getExecFile()
	 */
	@Override
	public IBinaryObject getExecFile() {
		return getBinaryFile();
	}
	
	public IBinaryObject getBinaryFile() {
		return fBinaryFile;
	}

	private void setExecFile(IBinaryObject file) {
		fBinaryFile = file;
	}
	
	private void setProject(IProject project) {
		fProject = project;
	}
	
	public IProject getProject() {
		return fProject;
	}
	
	/* (non-Javadoc)
	 * @see org.eclipse.cdt.debug.core.model.IExecFileInfo#getGlobals()
	 */
	@Override
	public IGlobalVariableDescriptor[] getGlobals() throws DebugException {
		ICDITarget cdiTarget = getCDITarget();
		// If the backend can give us the globals...
		boolean hasCDIGlobals = false;
		ArrayList<IGlobalVariableDescriptor> list = new ArrayList<IGlobalVariableDescriptor>();
		if (cdiTarget instanceof ICDITarget2) {
			ICDIGlobalVariableDescriptor[] cdiGlobals = ((ICDITarget2) cdiTarget).getGlobalVariables();
			hasCDIGlobals = cdiGlobals != null;
			if (hasCDIGlobals) {
				for (int i = 0; i < cdiGlobals.length; i++) {
					list.add(CVariableFactory.createGlobalVariableDescriptor(cdiGlobals[i].getName(), null));
				}				
			}
		}
		// otherwise ask the binary
		if (!hasCDIGlobals) {
			IBinaryObject file = getBinaryFile();
			if (file != null) {
				list.addAll(getCFileGlobals(file));
			}
		}
		return list.toArray(new IGlobalVariableDescriptor[list.size()]);			
	}

	private List<IGlobalVariableDescriptor> getCFileGlobals(IBinaryObject file) {
		ArrayList<IGlobalVariableDescriptor> list = new ArrayList<IGlobalVariableDescriptor>();
		ISymbol[] symbols = file.getSymbols();
		for (int i = 0; i < symbols.length; ++i) {
			if (symbols[i].getType() == ISymbol.VARIABLE) {
				list.add(CVariableFactory.createGlobalVariableDescriptor(symbols[i]));
			}
		}
		return list;
	}

	protected void setModuleManager(CModuleManager mm) {
		fModuleManager = mm;
	}

	protected CModuleManager getModuleManager() {
		return fModuleManager;
	}

	protected void disposeModuleManager() {
		fModuleManager.dispose();
		fModuleManager = null;
	}

	protected void setSignalManager(CSignalManager sm) {
		fSignalManager = sm;
	}

	protected CSignalManager getSignalManager() {
		return fSignalManager;
	}

	protected void disposeSignalManager() {
		fSignalManager.dispose();
	}

	protected void saveRegisterGroups() {
		fRegisterManager.save();
	}

	protected void disposeRegisterManager() {
		fRegisterManager.dispose();
	}

	protected void saveGlobalVariables() {
		fGlobalVariableManager.save();
	}

	protected void saveFormats() {
		fFormatManager.save();
	}

	protected void disposeGlobalVariableManager() {
		fGlobalVariableManager.dispose();
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.debug.core.model.IResumeWithoutSignal#canResumeWithoutSignal()
	 */
	@Override
	public boolean canResumeWithoutSignal() {
		// Check if the configuration supports this!!!
		return (canResume() && getCurrentStateInfo() instanceof ICDISignalReceived);
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.debug.core.model.IResumeWithoutSignal#resumeWithoutSignal()
	 */
	@Override
	public void resumeWithoutSignal() throws DebugException {
		if (!canResume())
			return;
		final CDebugElementState newState = CDebugElementState.RESUMING;
		changeState(newState);
		try {
			final ICDITarget cdiTarget = getCDITarget();
			if (cdiTarget != null) {
				cdiTarget.resume(false);
			}
		} catch (CDIException e) {
			if (getState() == newState) {
				restoreOldState();
			}
			targetRequestFailed(e.getMessage(), e);
		}
	}

	public CRegisterManager getRegisterManager() {
	    // Workaround for bug #309212. gdb 7.0 returns "No registers" error 
	    // at the beginning of the session.   
	    fRegisterManager.initialize();
		return fRegisterManager;
	}

	protected void setRegisterManager(CRegisterManager registerManager) {
		fRegisterManager = registerManager;
	}

	public IRegisterGroup[] getRegisterGroups(CStackFrame frame) throws DebugException {
		return getRegisterManager().getRegisterGroups(frame);
	}

	protected void disposeSourceManager() {
		ISourceLocator locator = getSourceLocator();
		if (locator instanceof IAdaptable) {
			IResourceChangeListener listener = (IResourceChangeListener)((IAdaptable)locator).getAdapter(IResourceChangeListener.class);
			if (listener != null)
				CCorePlugin.getWorkspace().removeResourceChangeListener(listener);
		}
	}

	protected void disposeSourceLookupPath() {
		ISourceLocator locator = getLaunch().getSourceLocator();
		if (locator instanceof ISourceLookupDirector) {
			ISourceLookupParticipant[] participants = ((ISourceLookupDirector)locator).getParticipants();
			for (int i = 0; i < participants.length; ++i) {
				if (participants[i] instanceof CSourceLookupParticipant) {
					((CSourceLookupParticipant)participants[i]).removeSourceLookupChangeListener(this);
				}
			}
		}
	}

	protected void saveMemoryBlocks() {
		getMemoryBlockRetrieval().save();
	}

	protected void disposeMemoryBlockRetrieval() {
		getMemoryBlockRetrieval().dispose();
	}

	protected CBreakpointManager getBreakpointManager() {
		return fBreakpointManager;
	}

	protected void setBreakpointManager(CBreakpointManager manager) {
		fBreakpointManager = manager;
	}

	protected void disposeBreakpointManager() {
		if (getBreakpointManager() != null)
			getBreakpointManager().dispose();
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
    public String toString() {
		String result = ""; //$NON-NLS-1$
		try {
			result = getName();
		} catch (DebugException e) {
		}
		return result;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.debug.core.model.ICDebugTarget#getDisassembly()
	 */
    @Override
	public IDisassembly getDisassembly() throws DebugException {
        return fDisassembly;
    }

    public DisassemblyRetrieval getDisassemblyRetrieval() {
        return fDisassemblyRetrieval;
    }

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.debug.core.model.ICDebugTarget#getSignals()
	 */
	@Override
	public ICSignal[] getSignals() throws DebugException {
		CSignalManager sm = getSignalManager();
		if (sm != null) {
			return sm.getSignals();
		}
		return new ICSignal[0];
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.debug.core.model.ICDebugTarget#hasSignals()
	 */
	@Override
	public boolean hasSignals() throws DebugException {
		CSignalManager sm = getSignalManager();
		if (sm != null) {
			return (sm.getSignals().length > 0);
		}
		return false;
	}

	private void createDisassembly() {
		this.fDisassembly = new Disassembly(this);
		this.fDisassemblyRetrieval = new DisassemblyRetrieval(this);
	}

	private void disposeDisassembly() {
        if (fDisassembly != null)
            fDisassembly.dispose();
        fDisassembly = null;
        if (fDisassemblyRetrieval != null)
            fDisassemblyRetrieval.dispose();
        fDisassemblyRetrieval = null;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.debug.core.model.IBreakpointTarget#getBreakpointAddress(org.eclipse.cdt.debug.core.model.ICLineBreakpoint)
	 */
	@Override
	public IAddress getBreakpointAddress(ICLineBreakpoint breakpoint) throws DebugException {
		return (getBreakpointManager() != null) ? getBreakpointManager().getBreakpointAddress(breakpoint) : getAddressFactory().getZero();
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.debug.core.model.ISteppingModeTarget#enableInstructionStepping(boolean)
	 */
	@Override
	public void enableInstructionStepping(boolean enabled) {
		fPreferences.setValue(PREF_INSTRUCTION_STEPPING_MODE, enabled);
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.debug.core.model.ISteppingModeTarget#isInstructionSteppingEnabled()
	 */
	@Override
	public boolean isInstructionSteppingEnabled() {
		return fPreferences.getBoolean(PREF_INSTRUCTION_STEPPING_MODE);
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.debug.core.model.ISteppingModeTarget#supportsInstructionStepping()
	 */
	@Override
	public boolean supportsInstructionStepping() {
		return getConfiguration().supportsInstructionStepping();
	}

	private void initializePreferences() {
		fPreferences = new Preferences();
		fPreferences.setDefault(PREF_INSTRUCTION_STEPPING_MODE, CDebugCorePlugin.getDefault().getPluginPreferences().getBoolean(ICDebugConstants.PREF_INSTRUCTION_STEP_MODE_ON));
	}

	private void disposePreferences() {
	    if (fPreferences != null) {
	        // persist current instruction stepping mode
	        CDebugCorePlugin.getDefault().getPluginPreferences().setValue(ICDebugConstants.PREF_INSTRUCTION_STEP_MODE_ON, fPreferences.getBoolean(PREF_INSTRUCTION_STEPPING_MODE));
            CDebugCorePlugin.getDefault().savePluginPreferences();
	    }
		fPreferences = null;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.debug.core.model.ITargetProperties#addPropertyChangeListener(org.eclipse.core.runtime.Preferences.IPropertyChangeListener)
	 */
	@Override
	public void addPropertyChangeListener(IPropertyChangeListener listener) {
		if (fPreferences != null)
			fPreferences.addPropertyChangeListener(listener);
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.debug.core.model.ITargetProperties#removePropertyChangeListener(org.eclipse.core.runtime.Preferences.IPropertyChangeListener)
	 */
	@Override
	public void removePropertyChangeListener(IPropertyChangeListener listener) {
		if (fPreferences != null)
			fPreferences.removePropertyChangeListener(listener);
	}

	protected CGlobalVariableManager getGlobalVariableManager() {
		return fGlobalVariableManager;
	}

	private void setGlobalVariableManager(CGlobalVariableManager globalVariableManager) {
		fGlobalVariableManager = globalVariableManager;
	}

	protected CSettingsManager getFormatManager() {
		return fFormatManager;
	}

	private void setFormatManager(CSettingsManager formatManager) {
		fFormatManager = formatManager;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.debug.core.model.ICDebugTarget#isPostMortem()
	 */
	@Override
	public boolean isPostMortem() {
		return false;
	}

	public IAddressFactory getAddressFactory() {
		if (fAddressFactory == null) {			
			// Ask CDI plug-in for the default AddressFactory.
			final ICDITarget cdiTarget = getCDITarget();
			if (cdiTarget instanceof ICDIAddressFactoryManagement) {
				fAddressFactory = ((ICDIAddressFactoryManagement) cdiTarget).getAddressFactory();
			}
			// And if that doesn't work, use the one from the file.
			if (fAddressFactory == null) {
				if (getExecFile() != null && getProject() != null) {
					IBinaryObject file = getBinaryFile();
					if (file != null) {
						fAddressFactory = file.getAddressFactory();
					}
				}
			}
			
			// As a last resort, fallback to 64 bit address factory
			if (fAddressFactory == null) {
				fAddressFactory = new Addr64Factory();
			}
		}
		return fAddressFactory;
	}

	private CMemoryBlockRetrievalExtension getMemoryBlockRetrieval() {
		return fMemoryBlockRetrieval;
	}

	private void setMemoryBlockRetrieval(CMemoryBlockRetrievalExtension memoryBlockRetrieval) {
		fMemoryBlockRetrieval = memoryBlockRetrieval;
	}

	private void changeState(CDebugElementState state) {
		setState(state);
		Iterator<CThread> it = getThreadList().iterator();
		while (it.hasNext()) {
			it.next().setState(state);
		}
	}

	protected void restoreOldState() {
		restoreState();
		Iterator<CThread> it = getThreadList().iterator();
		while (it.hasNext()) {
			it.next().restoreState();
		}
	}

	private void handleSymbolsLoaded(ICDISharedLibrary library) {
		getModuleManager().symbolsLoaded(library);
	}

	public ICGlobalVariable createGlobalVariable(IGlobalVariableDescriptor info) throws DebugException {
		ICDIVariableDescriptor vo = null;
		try {
			final ICDITarget cdiTarget = getCDITarget();
			if (cdiTarget != null) {
				vo = cdiTarget.getGlobalVariableDescriptors(info.getPath().lastSegment(), null, info.getName());
			}
		} catch (CDIException e) {
			throw new DebugException(new Status(IStatus.ERROR, CDIDebugModel.getPluginIdentifier(), DebugException.TARGET_REQUEST_FAILED, e.getMessage(), null));
		}
		return CVariableFactory.createGlobalVariable(this, info, vo);
	}

	@Override
	public void sourceContainersChanged(ISourceLookupDirector director) {
		setSourceLookupPath(director.getSourceContainers());
	}

	private void setSourceLookupPath(ISourceContainer[] containers) {
		// LinkedHashSet allows quick lookup and deterministic ordering. We need
		// the former to efficiently prevent infinite recursion
		LinkedHashSet<String> list = new LinkedHashSet<String>(containers.length);
		
		getSourceLookupPath(list, containers);
		try {
			final ICDITarget cdiTarget = getCDITarget();
			if (cdiTarget != null) {
				cdiTarget.setSourcePaths(list.toArray(new String[list.size()]));
			}
		} catch (CDIException e) {
			CDebugCorePlugin.log(e);
		}
	}

	private void getSourceLookupPath(LinkedHashSet<String> list, ISourceContainer[] containers) {
		for (ISourceContainer container : containers) {
			String pathToAdd = null;
			
			if (container instanceof CProjectSourceContainer) {
				IProject project = ((CProjectSourceContainer) container).getProject();
				if (project != null && project.exists()) {
					IPath location = project.getLocation();
					if (location != null) {
						pathToAdd = location.toPortableString();
					}
				}
			} else if (container instanceof ProjectSourceContainer) { // For backward compatibility
				IProject project = ((ProjectSourceContainer) container).getProject();
				if (project != null && project.exists()) {
					IPath location = project.getLocation();
					if (location != null) {
						pathToAdd = location.toPortableString();
					}
				}
			} else if (container instanceof FolderSourceContainer) {
				IContainer folderContainer = ((FolderSourceContainer) container).getContainer();
				if (folderContainer != null && folderContainer.exists()) {
					IPath location = folderContainer.getLocation();
					if (location != null) {
						pathToAdd = location.toPortableString();
					}
				}
			} if (container instanceof DirectorySourceContainer) {
				File dir = ((DirectorySourceContainer) container).getDirectory();
				if (dir != null && dir.exists()) {
					IPath path = new Path(dir.getAbsolutePath());
					pathToAdd = path.toPortableString();
				}
			}

			if (pathToAdd != null) {
				// 291912. Avoid infinite recursion
				if (list.contains(pathToAdd)) {
					continue;	
				}
				
				list.add(pathToAdd);
			}
			
			if (container.isComposite()) {
				try {
					getSourceLookupPath(list, container.getSourceContainers());
				} catch (CoreException e) {
					CDebugCorePlugin.log(e.getStatus());
				}
			}
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.debug.core.model.ICDebugTarget#getRegisterDescriptors()
	 */
	@Override
	public IRegisterDescriptor[] getRegisterDescriptors() throws DebugException {
		return getRegisterManager().getAllRegisterDescriptors();
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.debug.core.model.ICDebugTarget#addUserDefinedRegisterGroup(java.lang.String, org.eclipse.cdt.debug.core.model.IRegisterDescriptor[])
	 */
	@Override
	public void addRegisterGroup(String name, IRegisterDescriptor[] descriptors) {
		getRegisterManager().addRegisterGroup(name, descriptors);
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.debug.core.model.ICDebugTarget#removeRegisterGroups(org.eclipse.debug.core.model.IRegisterGroup[])
	 */
	@Override
	public void removeRegisterGroups(IRegisterGroup[] groups) {
		getRegisterManager().removeRegisterGroups(groups);
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.debug.core.model.ICDebugTarget#modifyRegisterGroup(org.eclipse.cdt.debug.core.model.IPersistableRegisterGroup, org.eclipse.cdt.debug.core.model.IRegisterDescriptor[])
	 */
	@Override
	public void modifyRegisterGroup(IPersistableRegisterGroup group, IRegisterDescriptor[] descriptors) {
		getRegisterManager().modifyRegisterGroup(group, descriptors);
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.debug.core.model.ICDebugTarget#restoreDefaultRegisterGroups()
	 */
	@Override
	public void restoreDefaultRegisterGroups() {
		getRegisterManager().restoreDefaults();
	}

	protected void skipBreakpoints(boolean enabled) {
		getBreakpointManager().skipBreakpoints(enabled);
	}

	/**
	 * 'stopExpression' is used solely for the error message if the request
	 * fails. Where to stop is dictated entirely by 'location'
	 * @param symbol
	 */
	private void stopAtLocation(ICDILocation location, String stopExpression) throws DebugException {
		try {
			setInternalTemporaryBreakpoint(location);
		} catch (CoreException e) {
			final ICDITarget cdiTarget = getCDITarget();
			boolean isTerminated = cdiTarget != null && cdiTarget.isTerminated();
			if (isTerminated) {
				String message = MessageFormat.format(CoreModelMessages.getString("CDebugTarget.0"), new String[]{ stopExpression }); //$NON-NLS-1$
				MultiStatus status = new MultiStatus(CDebugCorePlugin.getUniqueIdentifier(), IStatus.OK, message, null);
				status.add(e.getStatus());
				throw new DebugException(status);
			}
			String message = MessageFormat.format(CoreModelMessages.getString("CDebugTarget.2"), new String[]{ stopExpression, e.getStatus().getMessage() }); //$NON-NLS-1$
			IStatus newStatus = new Status(IStatus.WARNING, e.getStatus().getPlugin(), ICDebugInternalConstants.STATUS_CODE_QUESTION, message, null);
			if (!CDebugUtils.question(newStatus, this)) {
				throw new DebugException(new Status(IStatus.OK, e.getStatus().getPlugin(), e.getStatus().getCode(), e.getStatus().getMessage(), null));
			}
		}
	}
	
	protected void stopAtSymbol(String stopSymbol) throws DebugException {
		final ICDITarget cdiTarget = getCDITarget();
		if (cdiTarget != null) {
			ICDILocation location = cdiTarget.createFunctionLocation("", stopSymbol); //$NON-NLS-1$
			stopAtLocation(location, stopSymbol);
		}
	}

	protected void stopAtAddress(IAddress address) throws DebugException {
		final ICDITarget cdiTarget = getCDITarget();
		if (cdiTarget != null) {
			ICDIAddressLocation location = cdiTarget.createAddressLocation(address.getValue()); 
			stopAtLocation(location, address.toHexAddressString());
		}
	}
	
	protected void stopInMain() throws DebugException {
		String mainSymbol = ICDTLaunchConfigurationConstants.DEBUGGER_STOP_AT_MAIN_SYMBOL_DEFAULT;
		try {
			final ICDITarget cdiTarget = getCDITarget();
			if (cdiTarget != null) {
				mainSymbol = getLaunch().getLaunchConfiguration().getAttribute(ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN_SYMBOL, ICDTLaunchConfigurationConstants.DEBUGGER_STOP_AT_MAIN_SYMBOL_DEFAULT);
				ICDILocation location = cdiTarget.createFunctionLocation("", mainSymbol); //$NON-NLS-1$ 
				setInternalTemporaryBreakpoint(location);
			}
		} catch (CoreException e) {
			String message = MessageFormat.format(CoreModelMessages.getString("CDebugTarget.2"), new String[]{ mainSymbol, e.getStatus().getMessage() }); //$NON-NLS-1$
			IStatus newStatus = new Status(IStatus.WARNING, e.getStatus().getPlugin(), ICDebugInternalConstants.STATUS_CODE_QUESTION, message, null);
			if (!CDebugUtils.question(newStatus, this)) {
				terminate();
				throw new DebugException(new Status(IStatus.OK, e.getStatus().getPlugin(), e.getStatus().getCode(), e.getStatus().getMessage(), null));
			}
		}
	}

	@Override
	public boolean hasModules() throws DebugException {
		CModuleManager mm = getModuleManager();
		if (mm != null)
			return mm.hasModules();
		return false;
	}

	@Override
	public ICModule[] getModules() throws DebugException {
		CModuleManager mm = getModuleManager();
		if (mm != null)
			return mm.getModules();
		return new ICModule[0];
	}

	@Override
	public void loadSymbolsForAllModules() throws DebugException {
		CModuleManager mm = getModuleManager();
		if (mm != null)
			mm.loadSymbolsForAllModules();
	}

	public String getInternalID() {
		return internalD;
	}
}