/*******************************************************************************
 * Copyright (c) 2006, 2014 Wind River Systems and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     Wind River Systems - initial API and implementation
 *     Alvaro Sanchez-Leon (Ericsson) - Make Registers View specific to a frame (Bug 323552)
 *******************************************************************************/
package org.eclipse.cdt.dsf.debug.ui.viewmodel.register;

import java.util.Map;
import java.util.Set;
import java.util.concurrent.RejectedExecutionException;

import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor;
import org.eclipse.cdt.dsf.concurrent.CountingRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.DsfRunnable;
import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants;
import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor;
import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
import org.eclipse.cdt.dsf.datamodel.DMContexts;
import org.eclipse.cdt.dsf.datamodel.IDMContext;
import org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryChangedEvent;
import org.eclipse.cdt.dsf.debug.service.IRegisters;
import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterChangedDMEvent;
import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterDMContext;
import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterDMData;
import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterGroupDMData;
import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegistersChangedDMEvent;
import org.eclipse.cdt.dsf.debug.service.IRunControl.IResumedDMEvent;
import org.eclipse.cdt.dsf.debug.service.IRunControl.ISuspendedDMEvent;
import org.eclipse.cdt.dsf.debug.ui.viewmodel.ErrorLabelForeground;
import org.eclipse.cdt.dsf.debug.ui.viewmodel.ErrorLabelText;
import org.eclipse.cdt.dsf.debug.ui.viewmodel.IDebugVMConstants;
import org.eclipse.cdt.dsf.debug.ui.viewmodel.expression.AbstractExpressionVMNode;
import org.eclipse.cdt.dsf.debug.ui.viewmodel.numberformat.FormattedValueLabelText;
import org.eclipse.cdt.dsf.debug.ui.viewmodel.numberformat.FormattedValueRetriever;
import org.eclipse.cdt.dsf.debug.ui.viewmodel.numberformat.IFormattedValueVMContext;
import org.eclipse.cdt.dsf.debug.ui.viewmodel.update.ElementFormatEvent;
import org.eclipse.cdt.dsf.debug.ui.viewmodel.variable.VariableLabelFont;
import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin;
import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.cdt.dsf.ui.concurrent.ViewerDataRequestMonitor;
import org.eclipse.cdt.dsf.ui.viewmodel.VMDelta;
import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMProvider;
import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext;
import org.eclipse.cdt.dsf.ui.viewmodel.properties.IElementPropertiesProvider;
import org.eclipse.cdt.dsf.ui.viewmodel.properties.IPropertiesUpdate;
import org.eclipse.cdt.dsf.ui.viewmodel.properties.LabelAttribute;
import org.eclipse.cdt.dsf.ui.viewmodel.properties.LabelBackground;
import org.eclipse.cdt.dsf.ui.viewmodel.properties.LabelColumnInfo;
import org.eclipse.cdt.dsf.ui.viewmodel.properties.LabelForeground;
import org.eclipse.cdt.dsf.ui.viewmodel.properties.LabelImage;
import org.eclipse.cdt.dsf.ui.viewmodel.properties.LabelText;
import org.eclipse.cdt.dsf.ui.viewmodel.properties.PropertiesBasedLabelProvider;
import org.eclipse.cdt.dsf.ui.viewmodel.properties.VMDelegatingPropertiesUpdate;
import org.eclipse.cdt.dsf.ui.viewmodel.update.ICachingVMProvider;
import org.eclipse.cdt.dsf.ui.viewmodel.update.StaleDataLabelBackground;
import org.eclipse.cdt.dsf.ui.viewmodel.update.StaleDataLabelForeground;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.model.IExpression;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementCompareRequest;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementEditor;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementLabelProvider;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementMementoProvider;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementMementoRequest;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext;
import org.eclipse.debug.ui.DebugUITools;
import org.eclipse.debug.ui.IDebugModelPresentation;
import org.eclipse.debug.ui.IDebugUIConstants;
import org.eclipse.debug.ui.actions.IWatchExpressionFactoryAdapter2;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.ICellModifier;
import org.eclipse.jface.viewers.TextCellEditor;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.swt.widgets.Composite;

/**
 * @since 1.0
 */
public class RegisterVMNode extends AbstractExpressionVMNode
		implements IElementEditor, IElementLabelProvider, IElementMementoProvider, IElementPropertiesProvider {
	/**
	 * @since 2.0
	 */
	private static final String PROP_REGISTER_SHOW_TYPE_NAMES = "register_show_type_names"; //$NON-NLS-1$

	protected class RegisterVMC extends DMVMContext implements IFormattedValueVMContext {
		private IExpression fExpression;

		public RegisterVMC(IDMContext dmc) {
			super(dmc);
		}

		public void setExpression(IExpression expression) {
			fExpression = expression;
		}

		@Override
		@SuppressWarnings({ "rawtypes", "unchecked" })
		public Object getAdapter(Class adapter) {
			if (fExpression != null && adapter.isAssignableFrom(fExpression.getClass())) {
				return fExpression;
			} else if (adapter.isAssignableFrom(IWatchExpressionFactoryAdapter2.class)) {
				return getWatchExpressionFactory();
			} else {
				return super.getAdapter(adapter);
			}
		}

		@Override
		public boolean equals(Object other) {
			if (other instanceof RegisterVMC && super.equals(other)) {
				RegisterVMC otherReg = (RegisterVMC) other;
				return (otherReg.fExpression == null && fExpression == null)
						|| (otherReg.fExpression != null && otherReg.fExpression.equals(fExpression));
			}
			return false;
		}

		@Override
		public int hashCode() {
			return super.hashCode() + (fExpression != null ? fExpression.hashCode() : 0);
		}
	}

	protected class RegisterExpressionFactory implements IWatchExpressionFactoryAdapter2 {

		@Override
		public boolean canCreateWatchExpression(Object element) {
			return element instanceof RegisterVMC;
		}

		/**
		 * Expected format: GRP( GroupName ).REG( RegisterName )
		 */
		@Override
		public String createWatchExpression(Object element) throws CoreException {
			IRegisterGroupDMData groupData = getSyncRegisterDataAccess().getRegisterGroupDMData(element);
			IRegisterDMData registerData = getSyncRegisterDataAccess().getRegisterDMData(element);

			if (groupData != null && registerData != null) {
				StringBuffer exprBuf = new StringBuffer();

				exprBuf.append("GRP( "); //$NON-NLS-1$
				exprBuf.append(groupData.getName());
				exprBuf.append(" )"); //$NON-NLS-1$
				exprBuf.append(".REG( "); //$NON-NLS-1$
				exprBuf.append(registerData.getName());
				exprBuf.append(" )"); //$NON-NLS-1$

				return exprBuf.toString();
			}

			return null;
		}
	}

	private IWatchExpressionFactoryAdapter2 fRegisterExpressionFactory = null;
	final private SyncRegisterDataAccess fSyncRegisterDataAccess;

	/**
	 * The label provider delegate.  This VM node will delegate label updates to this provider
	 * which can be created by sub-classes.
	 *
	 * @since 2.0
	 */
	private IElementLabelProvider fLabelProvider;

	/**
	 * Retriever for formatted values configured for this VM node.
	 * @since 2.2
	 */
	private final FormattedValueRetriever fFormattedValueRetriever;

	public RegisterVMNode(AbstractDMVMProvider provider, DsfSession session, SyncRegisterDataAccess syncDataAccess) {
		super(provider, session, IRegisterDMContext.class);
		fSyncRegisterDataAccess = syncDataAccess;
		fLabelProvider = createLabelProvider();
		fFormattedValueRetriever = new FormattedValueRetriever(this, session, IRegisters.class,
				IRegisterDMContext.class);
	}

	private Object[] constructTypeObjects(Map<String, Object> properties) {
		int type = 0;
		if (Boolean.TRUE.equals(properties.get(IRegisterVMConstants.PROP_IS_FLOAT))) {
			type = 1;
		}

		int readAttr = 0;
		if (Boolean.TRUE.equals(properties.get(IRegisterVMConstants.PROP_IS_READABLE))) {
			readAttr = 1;
		} else if (Boolean.TRUE.equals(properties.get(IRegisterVMConstants.PROP_IS_READONCE))) {
			readAttr = 2;
		}

		int writeAttr = 0;
		if (Boolean.TRUE.equals(properties.get(IRegisterVMConstants.PROP_IS_WRITEABLE))) {
			writeAttr = 1;
		} else if (Boolean.TRUE.equals(properties.get(IRegisterVMConstants.PROP_IS_WRITEONCE))) {
			writeAttr = 2;
		}
		Object[] messageAttrs = new Object[] { type, readAttr, writeAttr };
		return messageAttrs;
	}

	/**
	 * Creates the label provider delegate.  This VM node will delegate label
	 * updates to this provider which can be created by sub-classes.
	 *
	 * @return Returns the label provider for this node.
	 *
	 * @since 2.0
	 */
	private LabelBackground columnIdValueBackground;
	private IPropertyChangeListener fPreferenceChangeListener;

	@Override
	public void dispose() {

		if (fPreferenceChangeListener != null) {
			DebugUITools.getPreferenceStore().removePropertyChangeListener(fPreferenceChangeListener);
		}

		super.dispose();

		fFormattedValueRetriever.dispose();
	}

	protected IElementLabelProvider createLabelProvider() {
		/*
		* Create background which is responsive to the preference color changes.
		*/
		columnIdValueBackground = new LabelBackground(
				DebugUITools.getPreferenceColor(IDebugUIConstants.PREF_CHANGED_VALUE_BACKGROUND).getRGB()) {
			{
				setPropertyNames(new String[] { IDebugVMConstants.PROP_FORMATTED_VALUE_ACTIVE_FORMAT_VALUE,
						ICachingVMProvider.PROP_IS_CHANGED_PREFIX
								+ IDebugVMConstants.PROP_FORMATTED_VALUE_ACTIVE_FORMAT_VALUE,
						IDebugVMConstants.PROP_FORMATTED_VALUE_ACTIVE_FORMAT, ICachingVMProvider.PROP_IS_CHANGED_PREFIX
								+ IDebugVMConstants.PROP_FORMATTED_VALUE_ACTIVE_FORMAT });
			}

			@Override
			public boolean isEnabled(IStatus status, java.util.Map<String, Object> properties) {
				Boolean activeFormatChanged = (Boolean) properties.get(ICachingVMProvider.PROP_IS_CHANGED_PREFIX
						+ IDebugVMConstants.PROP_FORMATTED_VALUE_ACTIVE_FORMAT);
				Boolean activeChanged = (Boolean) properties.get(ICachingVMProvider.PROP_IS_CHANGED_PREFIX
						+ IDebugVMConstants.PROP_FORMATTED_VALUE_ACTIVE_FORMAT_VALUE);
				return Boolean.TRUE.equals(activeChanged) && !Boolean.TRUE.equals(activeFormatChanged);
			}
		};

		if (fPreferenceChangeListener != null) {
			DebugUITools.getPreferenceStore().removePropertyChangeListener(fPreferenceChangeListener);
		}

		fPreferenceChangeListener = new IPropertyChangeListener() {
			@Override
			public void propertyChange(PropertyChangeEvent event) {
				if (event.getProperty().equals(IDebugUIConstants.PREF_CHANGED_VALUE_BACKGROUND)) {
					columnIdValueBackground.setBackground(
							DebugUITools.getPreferenceColor(IDebugUIConstants.PREF_CHANGED_VALUE_BACKGROUND).getRGB());
				}
			}
		};

		DebugUITools.getPreferenceStore().addPropertyChangeListener(fPreferenceChangeListener);

		PropertiesBasedLabelProvider provider = new PropertiesBasedLabelProvider();

		// The name column consists of the register name.
		provider.setColumnInfo(IDebugVMConstants.COLUMN_ID__NAME,
				new LabelColumnInfo(new LabelAttribute[] {
						new LabelText(MessagesForRegisterVM.RegisterVMNode_Name_column__text_format,
								new String[] { PROP_NAME }),
						new LabelImage(DebugUITools.getImageDescriptor(IDebugUIConstants.IMG_OBJS_REGISTER)),
						new StaleDataLabelForeground(), new VariableLabelFont(), }));

		// The description column contains a brief description of the register.
		provider.setColumnInfo(IDebugVMConstants.COLUMN_ID__DESCRIPTION,
				new LabelColumnInfo(new LabelAttribute[] {
						new LabelText(MessagesForRegisterVM.RegisterVMNode_Description_column__text_format,
								new String[] { IRegisterVMConstants.PROP_DESCRIPTION }),
						new StaleDataLabelForeground(), new VariableLabelFont(), }));

		// In the type column add information about register read/write/fload flags.
		provider.setColumnInfo(IDebugVMConstants.COLUMN_ID__TYPE,
				new LabelColumnInfo(new LabelAttribute[] { new LabelText(
						MessagesForRegisterVM.RegisterVMNode_Type_column__text_format,
						new String[] { IRegisterVMConstants.PROP_IS_FLOAT, IRegisterVMConstants.PROP_IS_READABLE,
								IRegisterVMConstants.PROP_IS_READONCE, IRegisterVMConstants.PROP_IS_WRITEABLE,
								IRegisterVMConstants.PROP_IS_WRITEONCE }) {
					@Override
					public void updateAttribute(ILabelUpdate update, int columnIndex, IStatus status,
							Map<String, Object> properties) {
						Object[] messageAttrs = constructTypeObjects(properties);
						try {
							update.setLabel(
									getMessageFormat().format(messageAttrs, new StringBuffer(), null).toString(),
									columnIndex);
						} catch (IllegalArgumentException e) {
							update.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, 0,
									"Failed formatting a message for column " + columnIndex + ", for update " + update, //$NON-NLS-1$//$NON-NLS-2$
									e));
						}
					}
				}, new StaleDataLabelForeground(), new VariableLabelFont(), }));

		// Value column shows the value in the active value format.
		//
		// In case of error, show the error message in the value column (instead of the usual "...".  This is needed
		// for the expressions view, where an invalid expression entered by the user is a normal use case.
		//
		// For changed value high-lighting check the value in the active format.  But if the format itself has changed,
		// ignore the value change.
		provider.setColumnInfo(IDebugVMConstants.COLUMN_ID__VALUE,
				new LabelColumnInfo(new LabelAttribute[] { new FormattedValueLabelText(), new ErrorLabelText(),
						new ErrorLabelForeground(), columnIdValueBackground, new StaleDataLabelForeground(),
						new VariableLabelFont(), }));

		// Expression column is visible only in the expressions view.  It shows the expression string that the user
		// entered.  Expression column images are the same as for the name column.
		provider.setColumnInfo(IDebugVMConstants.COLUMN_ID__EXPRESSION,
				new LabelColumnInfo(new LabelAttribute[] {
						new LabelText(MessagesForRegisterVM.RegisterVMNode_Expression_column__text_format,
								new String[] { PROP_ELEMENT_EXPRESSION }),
						new LabelImage(DebugUITools.getImageDescriptor(IDebugUIConstants.IMG_OBJS_REGISTER)),
						new StaleDataLabelForeground(), new VariableLabelFont(), }));

		provider.setColumnInfo(PropertiesBasedLabelProvider.ID_COLUMN_NO_COLUMNS,
				new LabelColumnInfo(new LabelAttribute[] { new FormattedValueLabelText(
						MessagesForRegisterVM.RegisterVMNode_No_columns__text_format,
						new String[] { PROP_NAME, IDebugVMConstants.PROP_FORMATTED_VALUE_ACTIVE_FORMAT_VALUE }) {
					@Override
					public boolean isEnabled(IStatus status, Map<String, Object> properties) {
						Boolean showTypeNames = (Boolean) properties.get(PROP_REGISTER_SHOW_TYPE_NAMES);
						return showTypeNames != null && !showTypeNames.booleanValue()
								&& super.isEnabled(status, properties);
					}
				}, new FormattedValueLabelText(MessagesForRegisterVM.RegisterVMNode_No_columns__text_format_with_type,
						new String[] { PROP_NAME, IDebugVMConstants.PROP_FORMATTED_VALUE_ACTIVE_FORMAT_VALUE,
								IRegisterVMConstants.PROP_IS_FLOAT, IRegisterVMConstants.PROP_IS_READABLE,
								IRegisterVMConstants.PROP_IS_READONCE, IRegisterVMConstants.PROP_IS_WRITEABLE,
								IRegisterVMConstants.PROP_IS_WRITEONCE, PROP_REGISTER_SHOW_TYPE_NAMES }) {
					@Override
					public void updateAttribute(ILabelUpdate update, int columnIndex, IStatus status,
							Map<String, Object> properties) {
						Object[] messageAttrs = constructTypeObjects(properties);
						Object[] combinedAttrs = new Object[messageAttrs.length + 2];
						combinedAttrs[0] = super.getPropertyValue(PROP_NAME, status, properties);
						combinedAttrs[1] = super.getPropertyValue(
								IDebugVMConstants.PROP_FORMATTED_VALUE_ACTIVE_FORMAT_VALUE, status, properties);
						for (int idx = 0; idx < messageAttrs.length; idx++) {
							combinedAttrs[idx + 2] = messageAttrs[idx];
						}
						try {
							update.setLabel(
									getMessageFormat().format(combinedAttrs, new StringBuffer(), null).toString(),
									columnIndex);
						} catch (IllegalArgumentException e) {
							update.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, 0,
									"Failed formatting a message for column " + columnIndex + ", for update " + update, //$NON-NLS-1$//$NON-NLS-2$
									e));
						}
					}

					@Override
					public boolean isEnabled(IStatus status, Map<String, Object> properties) {
						Boolean showTypeNames = (Boolean) properties.get(PROP_REGISTER_SHOW_TYPE_NAMES);
						return showTypeNames != null && showTypeNames.booleanValue()
								&& super.isEnabled(status, properties);
					}
				}, new ErrorLabelText(MessagesForRegisterVM.RegisterVMNode_No_columns__Error__text_format,
						new String[] { PROP_NAME }),
						new LabelImage(DebugUITools.getImageDescriptor(IDebugUIConstants.IMG_OBJS_REGISTER)),
						new LabelForeground(DebugUITools
								.getPreferenceColor(IDebugUIConstants.PREF_CHANGED_DEBUG_ELEMENT_COLOR).getRGB()) {
							{
								setPropertyNames(
										new String[] { IDebugVMConstants.PROP_FORMATTED_VALUE_ACTIVE_FORMAT_VALUE,
												ICachingVMProvider.PROP_IS_CHANGED_PREFIX
														+ IDebugVMConstants.PROP_FORMATTED_VALUE_ACTIVE_FORMAT_VALUE,
												IDebugVMConstants.PROP_FORMATTED_VALUE_ACTIVE_FORMAT,
												ICachingVMProvider.PROP_IS_CHANGED_PREFIX
														+ IDebugVMConstants.PROP_FORMATTED_VALUE_ACTIVE_FORMAT });
							}

							@Override
							public boolean isEnabled(IStatus status, java.util.Map<String, Object> properties) {
								Boolean activeFormatChanged = (Boolean) properties
										.get(ICachingVMProvider.PROP_IS_CHANGED_PREFIX
												+ IDebugVMConstants.PROP_FORMATTED_VALUE_ACTIVE_FORMAT);
								Boolean activeChanged = (Boolean) properties
										.get(ICachingVMProvider.PROP_IS_CHANGED_PREFIX
												+ IDebugVMConstants.PROP_FORMATTED_VALUE_ACTIVE_FORMAT_VALUE);
								return Boolean.TRUE.equals(activeChanged) && !Boolean.TRUE.equals(activeFormatChanged);
							}
						}, new StaleDataLabelBackground(), new VariableLabelFont(), }));

		return provider;
	}

	@Override
	public String toString() {
		return "RegisterVMNode(" + getSession().getId() + ")"; //$NON-NLS-1$ //$NON-NLS-2$
	}

	protected SyncRegisterDataAccess getSyncRegisterDataAccess() {
		return fSyncRegisterDataAccess;
	}

	/**
	 * @since 1.1
	 */
	public IWatchExpressionFactoryAdapter2 getWatchExpressionFactory() {
		if (fRegisterExpressionFactory == null) {
			fRegisterExpressionFactory = new RegisterExpressionFactory();
		}
		return fRegisterExpressionFactory;
	}

	@Override
	public void update(final ILabelUpdate[] updates) {
		fLabelProvider.update(updates);
	}

	/**
	 * Update the variable view properties.  The formatted values need to be
	 * updated in the VM executor thread while the rest of the properties is
	 * updated in the service session's executor thread.  The implementation
	 * splits the handling of the updates to accomplish that.
	 *
	 * @see IElementPropertiesProvider#update(IPropertiesUpdate[])
	 *
	 * @since 2.0
	 */
	@Override
	public void update(final IPropertiesUpdate[] updates) {
		final CountingRequestMonitor countingRm = new CountingRequestMonitor(ImmediateExecutor.getInstance(), null) {
			@Override
			protected void handleCompleted() {
				for (int i = 0; i < updates.length; i++) {
					updates[i].done();
				}
			}
		};
		int count = 0;

		fFormattedValueRetriever.update(updates, countingRm);
		count++;

		final IPropertiesUpdate[] subUpdates = new IPropertiesUpdate[updates.length];
		for (int i = 0; i < updates.length; i++) {
			final IPropertiesUpdate update = updates[i];
			subUpdates[i] = new VMDelegatingPropertiesUpdate(update, countingRm);
			count++;
		}
		countingRm.setDoneCount(count);

		try {
			getSession().getExecutor().execute(new DsfRunnable() {
				@Override
				public void run() {
					updatePropertiesInSessionThread(subUpdates);
				}
			});
		} catch (RejectedExecutionException e) {
			for (IPropertiesUpdate subUpdate : subUpdates) {
				subUpdate.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED,
						"Session executor shut down " + getSession().getExecutor(), e)); //$NON-NLS-1$
				subUpdate.done();
			}
		}
	}

	//
	//  @param return-value Boolean.TRUE  --> Show Types ICON is     selected/depressed
	//  @param return-value Boolean.FALSE --> Show Types ICON is not selected/depressed
	//
	private Boolean getShowTypeNamesState(IPresentationContext context) {
		Boolean attribute = (Boolean) context.getProperty(IDebugModelPresentation.DISPLAY_VARIABLE_TYPE_NAMES);

		if (attribute != null) {
			return attribute;
		}

		return Boolean.FALSE;
	}

	/**
	 * @since 2.0
	 */
	@ConfinedToDsfExecutor("getSession().getExecutor()")
	protected void updatePropertiesInSessionThread(final IPropertiesUpdate[] updates) {
		IRegisters service = getServicesTracker().getService(IRegisters.class, null);

		// Create a counting request monitor to coordinate various activities
		// on the updated objects. Though the update objects will be given to
		// various ViewerDataRequestMonitors, such monitors must make sure to
		// not mark the update objects complete. That needs to be left to the
		// following monitor.
		final CountingRequestMonitor countingRm = new CountingRequestMonitor(ImmediateExecutor.getInstance(), null) {
			@Override
			protected void handleCompleted() {
				for (final IPropertiesUpdate update : updates) {
					update.done();
				}
			}
		};
		int count = 0;

		for (final IPropertiesUpdate update : updates) {
			IExpression expression = (IExpression) DebugPlugin.getAdapter(update.getElement(), IExpression.class);
			if (expression != null) {
				update.setProperty(AbstractExpressionVMNode.PROP_ELEMENT_EXPRESSION, expression.getExpressionText());
			}

			final IRegisterDMContext dmc = findDmcInPath(update.getViewerInput(), update.getElementPath(),
					IRegisterDMContext.class);
			if (dmc == null || service == null) {
				handleFailedUpdate(update);
				continue;
			}

			// Capture the current "Show Type Names" ICON state in case there are no columns.
			if (update.getProperties().contains(PROP_REGISTER_SHOW_TYPE_NAMES)) {
				update.setProperty(PROP_REGISTER_SHOW_TYPE_NAMES,
						getShowTypeNamesState(update.getPresentationContext()));
			}

			service.getRegisterData(dmc,
					// Use the ViewerDataRequestMonitor in order to propagate the update's cancel request. Use an immediate
					// executor to avoid the possibility of a rejected execution exception.
					new ViewerDataRequestMonitor<IRegisterDMData>(getSession().getExecutor(), update) {
						@Override
						protected void handleCompleted() {
							if (isSuccess()) {
								fillRegisterDataProperties(update, getData());
							} else {
								update.setStatus(getStatus());
							}
							countingRm.done();

							// Note: we must not call the update's done method
						}
					});
			count++;
		}
		countingRm.setDoneCount(count);
	}

	/**
	 * @since 2.0
	 */
	@ConfinedToDsfExecutor("getSession().getExecutor()")
	protected void fillRegisterDataProperties(IPropertiesUpdate update, IRegisterDMData data) {
		update.setProperty(PROP_NAME, data.getName());
		update.setProperty(IRegisterVMConstants.PROP_DESCRIPTION, data.getDescription());
		update.setProperty(IRegisterVMConstants.PROP_IS_FLOAT, data.isFloat());
		update.setProperty(IRegisterVMConstants.PROP_IS_READABLE, data.isReadable());
		update.setProperty(IRegisterVMConstants.PROP_IS_READONCE, data.isReadOnce());
		update.setProperty(IRegisterVMConstants.PROP_IS_WRITEABLE, data.isWriteable());
		update.setProperty(IRegisterVMConstants.PROP_IS_WRITEONCE, data.isWriteOnce());

		/*
		 * If this node has an expression then it has already been filled in by the higher
		 * level logic. If not then we need to supply something.  In the  previous version
		 * ( pre-property based ) we supplied the name. So we will do that here also.
		 */
		IExpression expression = (IExpression) DebugPlugin.getAdapter(update.getElement(), IExpression.class);
		if (expression == null) {
			update.setProperty(AbstractExpressionVMNode.PROP_ELEMENT_EXPRESSION, data.getName());
		}
	}

	@Override
	public void update(IHasChildrenUpdate[] updates) {
		// As an optimization, always indicate that register groups have
		// children.
		for (IHasChildrenUpdate update : updates) {
			update.setHasChilren(true);
			update.done();
		}
	}

	@Override
	protected void updateElementsInSessionThread(final IChildrenUpdate update) {

		IRegisters regService = getServicesTracker().getService(IRegisters.class);

		if (regService == null) {
			handleFailedUpdate(update);
			return;
		}

		regService.getRegisters(createCompositeDMVMContext(update),
				new ViewerDataRequestMonitor<IRegisterDMContext[]>(getSession().getExecutor(), update) {
					@Override
					public void handleCompleted() {
						if (!isSuccess()) {
							handleFailedUpdate(update);
							return;
						}
						fillUpdateWithVMCs(update, getData());
						update.done();
					}
				});
	}

	@Override
	protected IDMVMContext createVMContext(IDMContext dmc) {
		return new RegisterVMC(dmc);
	}

	@Override
	public int getDeltaFlags(Object e) {
		if (e instanceof ISuspendedDMEvent || e instanceof IResumedDMEvent || e instanceof IMemoryChangedEvent
				|| e instanceof IRegistersChangedDMEvent
				|| (e instanceof PropertyChangeEvent && (((PropertyChangeEvent) e)
						.getProperty() == IDebugVMConstants.PROP_FORMATTED_VALUE_FORMAT_PREFERENCE
						|| ((PropertyChangeEvent) e)
								.getProperty() == IDebugModelPresentation.DISPLAY_VARIABLE_TYPE_NAMES))) {
			return IModelDelta.CONTENT;
		}

		if (e instanceof IRegisterChangedDMEvent) {
			return IModelDelta.STATE;
		}

		if (e instanceof ElementFormatEvent) {
			int depth = ((ElementFormatEvent) e).getApplyDepth();
			if (depth == 0)
				return IModelDelta.NO_CHANGE;
			if (depth == 1)
				return IModelDelta.STATE;
			return IModelDelta.CONTENT;
		}

		return IModelDelta.NO_CHANGE;
	}

	@Override
	public void buildDelta(Object e, VMDelta parentDelta, int nodeOffset, RequestMonitor rm) {
		// The following events can affect any register's values,
		// refresh the contents of the parent element (i.e. all the registers).
		if (e instanceof ISuspendedDMEvent || e instanceof IResumedDMEvent || e instanceof IMemoryChangedEvent
				|| e instanceof IRegistersChangedDMEvent
				|| (e instanceof PropertyChangeEvent && (((PropertyChangeEvent) e)
						.getProperty() == IDebugVMConstants.PROP_FORMATTED_VALUE_FORMAT_PREFERENCE
						|| ((PropertyChangeEvent) e)
								.getProperty() == IDebugModelPresentation.DISPLAY_VARIABLE_TYPE_NAMES))) {
			// Create a delta that the whole register group has changed.
			parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.CONTENT);
		}

		if (e instanceof IRegisterChangedDMEvent) {
			parentDelta.addNode(createVMContext(((IRegisterChangedDMEvent) e).getDMContext()), IModelDelta.STATE);
		} else if (e instanceof ElementFormatEvent) {
			int depth = ((ElementFormatEvent) e).getApplyDepth();
			if (depth != 0) {
				int deltaType = IModelDelta.CONTENT;
				if (depth == 1)
					deltaType = IModelDelta.STATE;

				Set<Object> elements = ((ElementFormatEvent) e).getElements();
				for (Object elem : elements) {
					parentDelta.addNode(elem, deltaType);
				}
			}
		}

		rm.done();
	}

	/**
	 * Expected format: GRP( GroupName ).REG( RegisterName )
	 *              or: $RegisterName
	 */
	@Override
	public boolean canParseExpression(IExpression expression) {
		return parseExpressionForRegisterName(expression.getExpressionText()) != null;
	}

	private String parseExpressionForRegisterName(String expression) {
		if (expression.startsWith("GRP(")) { //$NON-NLS-1$
			/*
			 * Get the group portion.
			 */
			int startIdx = "GRP(".length(); //$NON-NLS-1$
			int endIdx = expression.indexOf(')', startIdx);
			if (startIdx == -1 || endIdx == -1) {
				return null;
			}
			String remaining = expression.substring(endIdx + 1);
			if (!remaining.startsWith(".REG(")) { //$NON-NLS-1$
				return null;
			}

			/*
			 * Get the register portion.
			 */
			startIdx = ".REG(".length(); //$NON-NLS-1$
			endIdx = remaining.indexOf(')', startIdx);
			if (startIdx == -1 || endIdx == -1) {
				return null;
			}
			String regName = remaining.substring(startIdx, endIdx);
			return regName.trim();
		} else if (expression.startsWith("$")) { //$NON-NLS-1$
			/*
			 * At this point I am leaving this code here to represent the register case. To do this
			 * correctly would be to use the findRegister function and upgrade the register service
			 * to deal with registers that  do not have a specified group parent context.  I do not
			 * have the time for this right now.  So by saying we do not handle this the Expression
			 * VM node will take it and pass it to the debug engine  as a generic expression.  Most
			 * debug engines ( GDB included )  have an inherent knowledge  of the core registers as
			 * part of their expression evaluation  and will respond with a flat value for the reg.
			 * This is not totally complete in that you should be able to express  a register which
			 * has bit fields for example and the bit fields should be expandable in the expression
			 * view. With this method it will just appear to have a single value and no sub-fields.
			 * I will file a defect/enhancement  for this to mark it.  This comment will act as the
			 * place-holder for the future work.
			 */
			return null;
		}

		return null;
	}

	@Override
	protected void testElementForExpression(Object element, IExpression expression,
			final DataRequestMonitor<Boolean> rm) {
		if (!(element instanceof IDMVMContext)) {
			rm.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_HANDLE,
					"Invalid context", null)); //$NON-NLS-1$
			rm.done();
			return;
		}
		final IRegisterDMContext dmc = DMContexts.getAncestorOfType(((IDMVMContext) element).getDMContext(),
				IRegisterDMContext.class);
		if (dmc == null) {
			rm.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_HANDLE,
					"Invalid context", null)); //$NON-NLS-1$
			rm.done();
			return;
		}

		final String regName = parseExpressionForRegisterName(expression.getExpressionText());
		try {
			getSession().getExecutor().execute(new DsfRunnable() {
				@Override
				public void run() {
					IRegisters registersService = getServicesTracker().getService(IRegisters.class);
					if (registersService != null) {
						registersService.getRegisterData(dmc,
								new DataRequestMonitor<IRegisterDMData>(ImmediateExecutor.getInstance(), rm) {
									@Override
									protected void handleSuccess() {
										rm.setData(getData().getName().equals(regName));
										rm.done();
									}
								});
					} else {
						rm.setStatus(new Status(IStatus.WARNING, DsfUIPlugin.PLUGIN_ID,
								IDsfStatusConstants.INVALID_STATE, "Register service not available", null)); //$NON-NLS-1$
						rm.done();
					}
				}
			});
		} catch (RejectedExecutionException e) {
			rm.setStatus(new Status(IStatus.WARNING, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_STATE,
					"DSF session shut down", null)); //$NON-NLS-1$
			rm.done();
		}
	}

	@Override
	protected void associateExpression(Object element, IExpression expression) {
		if (element instanceof RegisterVMC) {
			((RegisterVMC) element).setExpression(expression);
		}
	}

	@Override
	public int getDeltaFlagsForExpression(IExpression expression, Object event) {
		if (event instanceof IRegisterChangedDMEvent || event instanceof IMemoryChangedEvent
				|| (event instanceof PropertyChangeEvent && (((PropertyChangeEvent) event)
						.getProperty() == IDebugVMConstants.PROP_FORMATTED_VALUE_FORMAT_PREFERENCE
						|| ((PropertyChangeEvent) event)
								.getProperty() == IDebugModelPresentation.DISPLAY_VARIABLE_TYPE_NAMES))) {
			return IModelDelta.STATE;
		}

		if (event instanceof IRegistersChangedDMEvent || event instanceof ISuspendedDMEvent) {
			return IModelDelta.CONTENT;
		}

		return IModelDelta.NO_CHANGE;
	}

	@Override
	public void buildDeltaForExpression(IExpression expression, int elementIdx, Object event, VMDelta parentDelta,
			TreePath path, RequestMonitor rm) {
		// If the register definition has changed, refresh all the
		// expressions in the expression manager.  This is because some
		// expressions that were previously invalid, may now represent new
		// registers.
		if (event instanceof IRegistersChangedDMEvent) {
			parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.CONTENT);
		}

		// Always refresh the contents of the view upon suspended event.
		if (event instanceof ISuspendedDMEvent) {
			parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.CONTENT);
		}

		rm.done();
	}

	@Override
	public void buildDeltaForExpressionElement(Object element, int elementIdx, Object event, VMDelta parentDelta,
			final RequestMonitor rm) {
		// The following events can affect register values, refresh the state
		// of the expression.
		if (event instanceof IRegisterChangedDMEvent || event instanceof IMemoryChangedEvent
				|| (event instanceof PropertyChangeEvent && ((PropertyChangeEvent) event)
						.getProperty() == IDebugVMConstants.PROP_FORMATTED_VALUE_FORMAT_PREFERENCE)) {
			parentDelta.addNode(element, IModelDelta.STATE);
		}

		rm.done();
	}

	@Override
	public CellEditor getCellEditor(IPresentationContext context, String columnId, Object element, Composite parent) {
		if (IDebugVMConstants.COLUMN_ID__EXPRESSION.equals(columnId)) {
			return new TextCellEditor(parent);
		} else if (IDebugVMConstants.COLUMN_ID__VALUE.equals(columnId)) {
			/*
			 *   See if the register is writable and if so we will created a
			 *   cell editor for it.
			 */
			IRegisterDMData regData = getSyncRegisterDataAccess().readRegister(element);

			if (regData != null && regData.isWriteable()) {
				return new TextCellEditor(parent);
			}
		}
		return null;
	}

	@Override
	public ICellModifier getCellModifier(IPresentationContext context, Object element) {
		return new RegisterCellModifier(getDMVMProvider(), getSyncRegisterDataAccess());
	}

	private final String MEMENTO_NAME = "REGISTER_MEMENTO_NAME"; //$NON-NLS-1$

	@Override
	public void compareElements(IElementCompareRequest[] requests) {
		for (final IElementCompareRequest request : requests) {
			final IRegisterDMContext regDmc = findDmcInPath(request.getViewerInput(), request.getElementPath(),
					IRegisterDMContext.class);
			final String mementoName = request.getMemento().getString(MEMENTO_NAME);
			if (regDmc == null || mementoName == null) {
				request.done();
				continue;
			}

			//  Now go get the model data for the single register group found.
			try {
				getSession().getExecutor().execute(new DsfRunnable() {
					@Override
					public void run() {
						final IRegisters regService = getServicesTracker().getService(IRegisters.class);
						if (regService != null) {
							regService.getRegisterData(regDmc,
									new DataRequestMonitor<IRegisterDMData>(regService.getExecutor(), null) {
										@Override
										protected void handleCompleted() {
											if (getStatus().isOK()) {
												// Now make sure the register group is the one we want.
												request.setEqual(mementoName.equals("Register." + getData().getName())); //$NON-NLS-1$
											}
											request.done();
										}
									});
						} else {
							request.done();
						}
					}
				});
			} catch (RejectedExecutionException e) {
				request.done();
			}
		}
	}

	@Override
	public void encodeElements(IElementMementoRequest[] requests) {
		for (final IElementMementoRequest request : requests) {
			final IRegisterDMContext regDmc = findDmcInPath(request.getViewerInput(), request.getElementPath(),
					IRegisterDMContext.class);
			if (regDmc == null) {
				request.done();
				continue;
			}

			//  Now go get the model data for the single register group found.
			try {
				getSession().getExecutor().execute(new DsfRunnable() {
					@Override
					public void run() {
						final IRegisters regService = getServicesTracker().getService(IRegisters.class);
						if (regService != null) {
							regService.getRegisterData(regDmc,
									new DataRequestMonitor<IRegisterDMData>(regService.getExecutor(), null) {
										@Override
										protected void handleCompleted() {
											if (getStatus().isOK()) {
												// Now make sure the register group is the one we want.
												request.getMemento().putString(MEMENTO_NAME,
														"Register." + getData().getName()); //$NON-NLS-1$
											}
											request.done();
										}
									});
						} else {
							request.done();
						}
					}
				});
			} catch (RejectedExecutionException e) {
				request.done();
			}
		}
	}
}
