diff options
author | Pawel Piech | 2010-09-24 18:58:49 +0000 |
---|---|---|
committer | Pawel Piech | 2010-09-24 18:58:49 +0000 |
commit | 4d3edd80a2216db9e44594bc2b001410392147f4 (patch) | |
tree | aead2d9e55ec05d9edd459f5aaddd2f104d210ed | |
parent | de6322ba9cc997cda01596d337f44c00cf636183 (diff) | |
download | org.eclipse.cdt-4d3edd80a2216db9e44594bc2b001410392147f4.tar.gz org.eclipse.cdt-4d3edd80a2216db9e44594bc2b001410392147f4.tar.xz org.eclipse.cdt-4d3edd80a2216db9e44594bc2b001410392147f4.zip |
Bug 321932 - [update policy] When in manual mode, switching number format should still show value in other formats if value is cached.
44 files changed, 3953 insertions, 841 deletions
diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/numberformat/detail/NumberFormatDetailPane.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/numberformat/detail/NumberFormatDetailPane.java index 5b7e54bf394..3f6fd2c8060 100644 --- a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/numberformat/detail/NumberFormatDetailPane.java +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/internal/ui/viewmodel/numberformat/detail/NumberFormatDetailPane.java @@ -323,7 +323,7 @@ public class NumberFormatDetailPane implements IDetailPane2, IAdaptable, IProper IDebugVMConstants.PROP_FORMATTED_VALUE_AVAILABLE_FORMATS); if (formats != null) { for (String format : formats) { - properties.add(FormattedValueVMUtil.getPropertyForFormatId(format)); + properties.add(FormattedValueVMUtil.getPropertyForFormatId(format, null)); } } @@ -341,7 +341,7 @@ public class NumberFormatDetailPane implements IDetailPane2, IAdaptable, IProper finalResult.append(SPACES); finalResult.append( FormattedValueVMUtil.getFormatLabel(formatId) ); finalResult.append(FORMAT_SEPARATOR); - finalResult.append( getData().get(FormattedValueVMUtil.getPropertyForFormatId(formatId)) ); + finalResult.append( getData().get(FormattedValueVMUtil.getPropertyForFormatId(formatId, null)) ); if ( i < formats.length + 1 ) { finalResult.append(CRLF); } diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/ErrorLabelForeground.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/ErrorLabelForeground.java new file mode 100644 index 00000000000..a7e80fc7ba8 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/ErrorLabelForeground.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2010 Wind River 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.ui.viewmodel; + +import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; +import org.eclipse.cdt.dsf.ui.viewmodel.properties.LabelForeground; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.jface.preference.JFacePreferences; +import org.eclipse.jface.resource.ColorRegistry; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.themes.ITheme; +import org.eclipse.ui.themes.IThemeManager; + +/** + * Label attribute that sets the label color to the standard workbench + * error color. The color is activated when the property update contains + * a status with error codes: {@link IDsfStatusConstants#INTERNAL_ERROR}, + * {@link IDsfStatusConstants#REQUEST_FAILED}, or + * {@link IDsfStatusConstants#NOT_SUPPORTED}. + * + * @since 2.2 + */ +public class ErrorLabelForeground extends LabelForeground { + + private static final RGB DEFAULT_COLOR = new RGB(255, 0, 0); + + public ErrorLabelForeground() { + super(DEFAULT_COLOR); + } + + @Override + public boolean isEnabled(IStatus status, java.util.Map<String,Object> properties) { + return !status.isOK() && status.getCode() >= IDsfStatusConstants.NOT_SUPPORTED; + } + + @Override + public RGB getForeground() { + IThemeManager themeManager = PlatformUI.getWorkbench().getThemeManager(); + ITheme currentTheme = themeManager.getCurrentTheme(); + + ColorRegistry colorRegistry = currentTheme.getColorRegistry(); + + Color color = colorRegistry.get(JFacePreferences.ERROR_COLOR); + + if (color != null) { + return color.getRGB(); + } + return super.getForeground(); + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/ErrorLabelText.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/ErrorLabelText.java index 09e0e7ead1c..0dfec709a1e 100644 --- a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/ErrorLabelText.java +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/ErrorLabelText.java @@ -16,6 +16,9 @@ import org.eclipse.cdt.dsf.ui.viewmodel.properties.LabelText; import org.eclipse.core.runtime.IStatus; /** + * Label attribute that fills in the text of an error status into the label + * column. + * * @since 2.0 */ public class ErrorLabelText extends LabelText { @@ -23,16 +26,14 @@ public class ErrorLabelText extends LabelText { protected final static String PROP_ERROR_MESSAGE = "error_message"; //$NON-NLS-1$ public ErrorLabelText() { - this( - MessagesForDebugVM.ErrorLabelText__text_format, - new String[] { PROP_ERROR_MESSAGE }); + this(MessagesForDebugVM.ErrorLabelText__text_format, new String[] {}); } public ErrorLabelText(String formatPattern, String[] propertyNames) { - super(formatPattern, addActiveFormatPropertyNames(propertyNames)); + super(formatPattern, addErrorMessageProperty(propertyNames)); } - private static String[] addActiveFormatPropertyNames(String[] propertyNames) { + private static String[] addErrorMessageProperty(String[] propertyNames) { String[] newPropertyNames = new String[propertyNames.length + 1]; System.arraycopy(propertyNames, 0, newPropertyNames, 0, propertyNames.length); newPropertyNames[propertyNames.length + 0] = PROP_ERROR_MESSAGE; @@ -42,12 +43,24 @@ public class ErrorLabelText extends LabelText { @Override protected Object getPropertyValue(String propertyName, IStatus status, Map<String, Object> properties) { if (PROP_ERROR_MESSAGE.equals(propertyName)) { - return status.getMessage().replaceAll( - "\n", MessagesForDebugVM.ErrorLabelText_Error_message__text_page_break_delimiter); //$NON-NLS-1$ + if (status.getChildren().length < 2) { + return replaceNewlines(status.getMessage()); + } else { + StringBuffer buf = new StringBuffer( status.getMessage() ); + for (IStatus childStatus : status.getChildren()) { + buf.append(MessagesForDebugVM.ErrorLabelText_Error_message__text_page_break_delimiter); + buf.append( replaceNewlines(childStatus.getMessage()) ); + } + return buf.toString(); + } } return super.getPropertyValue(propertyName, status, properties); } + private String replaceNewlines(String message) { + return message.replaceAll("\n", MessagesForDebugVM.ErrorLabelText_Error_message__text_page_break_delimiter); //$NON-NLS-1$ + } + @Override public boolean checkProperty(String propertyName, IStatus status, Map<String,Object> properties) { if (PROP_ERROR_MESSAGE.equals(propertyName)) { diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/MessagesForDebugVM.properties b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/MessagesForDebugVM.properties index d043fac2d3e..741813118c0 100644 --- a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/MessagesForDebugVM.properties +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/MessagesForDebugVM.properties @@ -14,4 +14,4 @@ ErrorLabelText__text_format=Error: {0} # If multiple errors are shown in the combined error message, this # string is used as a separator between them. -ErrorLabelText_Error_message__text_page_break_delimiter= \
\ No newline at end of file +ErrorLabelText_Error_message__text_page_break_delimiter= \\
\ No newline at end of file diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/numberformat/FormattedValueLabelText.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/numberformat/FormattedValueLabelText.java index 77fd4d9fc2c..16963873297 100644 --- a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/numberformat/FormattedValueLabelText.java +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/numberformat/FormattedValueLabelText.java @@ -10,45 +10,62 @@ *******************************************************************************/ package org.eclipse.cdt.dsf.debug.ui.viewmodel.numberformat; -import com.ibm.icu.text.MessageFormat; import java.util.Map; import org.eclipse.cdt.dsf.debug.ui.viewmodel.IDebugVMConstants; import org.eclipse.cdt.dsf.ui.viewmodel.properties.LabelText; import org.eclipse.core.runtime.IStatus; +import com.ibm.icu.text.MessageFormat; + /** - * + * Label attribute that fills in the formatted value text using the active + * number format for the view. * * @since 2.0 */ public class FormattedValueLabelText extends LabelText { + private final String fPropertyPrefix; + private final String PROP_ACTIVE_FORMAT; + private final String PROP_ACTIVE_FORMAT_VALUE; + public FormattedValueLabelText() { - this( - MessagesForNumberFormat.FormattedValueLabelText__text_format, - new String[] { IDebugVMConstants.PROP_FORMATTED_VALUE_ACTIVE_FORMAT_VALUE }); + this(MessagesForNumberFormat.FormattedValueLabelText__text_format, new String[0], ""); //$NON-NLS-1$ + } + + public FormattedValueLabelText(String popertyPrefix) { + this(MessagesForNumberFormat.FormattedValueLabelText__text_format, new String[0], popertyPrefix); } public FormattedValueLabelText(String formatPattern, String[] propertyNames) { - super(formatPattern, addActiveFormatPropertyNames(propertyNames)); + this(formatPattern, propertyNames, ""); //$NON-NLS-1$ } - - private static String[] addActiveFormatPropertyNames(String[] propertyNames) { - String[] newPropertyNames = new String[propertyNames.length + 2]; + + public FormattedValueLabelText(String formatPattern, String[] propertyNames, String propertyPrefix) { + super(formatPattern, addActiveFormatPropertyNames(propertyNames, propertyPrefix)); + fPropertyPrefix = propertyPrefix; + PROP_ACTIVE_FORMAT = (fPropertyPrefix + IDebugVMConstants.PROP_FORMATTED_VALUE_ACTIVE_FORMAT).intern(); + PROP_ACTIVE_FORMAT_VALUE = (fPropertyPrefix + IDebugVMConstants.PROP_FORMATTED_VALUE_ACTIVE_FORMAT_VALUE).intern(); + } + + private static String[] addActiveFormatPropertyNames(String[] propertyNames, String prefix) { + String[] newPropertyNames = new String[propertyNames.length + 4]; System.arraycopy(propertyNames, 0, newPropertyNames, 0, propertyNames.length); - newPropertyNames[propertyNames.length + 0] = IDebugVMConstants.PROP_FORMATTED_VALUE_ACTIVE_FORMAT; - newPropertyNames[propertyNames.length + 1] = IDebugVMConstants.PROP_FORMATTED_VALUE_FORMAT_PREFERENCE; + newPropertyNames[propertyNames.length + 0] = (prefix + IDebugVMConstants.PROP_FORMATTED_VALUE_ACTIVE_FORMAT_VALUE).intern(); + newPropertyNames[propertyNames.length + 1] = (prefix + IDebugVMConstants.PROP_FORMATTED_VALUE_ACTIVE_FORMAT).intern(); + newPropertyNames[propertyNames.length + 2] = (prefix + IDebugVMConstants.PROP_FORMATTED_VALUE_AVAILABLE_FORMATS).intern(); + newPropertyNames[propertyNames.length + 3] = IDebugVMConstants.PROP_FORMATTED_VALUE_FORMAT_PREFERENCE; return newPropertyNames; } @Override protected Object getPropertyValue(String propertyName, IStatus status, Map<String, Object> properties) { // If the format is not the same as the preferred format, include it in the value string. - if ( IDebugVMConstants.PROP_FORMATTED_VALUE_ACTIVE_FORMAT_VALUE.equals(propertyName) ) { - Object activeFormat = properties.get(IDebugVMConstants.PROP_FORMATTED_VALUE_ACTIVE_FORMAT); + if ( PROP_ACTIVE_FORMAT_VALUE.equals(propertyName) ) { + Object activeFormat = properties.get(PROP_ACTIVE_FORMAT); Object preferredFormat = properties.get(IDebugVMConstants.PROP_FORMATTED_VALUE_FORMAT_PREFERENCE); - Object value = properties.get(IDebugVMConstants.PROP_FORMATTED_VALUE_ACTIVE_FORMAT_VALUE); + Object value = properties.get(PROP_ACTIVE_FORMAT_VALUE); if (value != null && activeFormat != null && !activeFormat.equals(preferredFormat)) { return MessageFormat.format( MessagesForNumberFormat.FormattedValueLabelText__Value__text_format, @@ -63,8 +80,8 @@ public class FormattedValueLabelText extends LabelText { @Override public boolean isEnabled(IStatus status, Map<String, Object> properties) { for (String property : getPropertyNames()) { - if ( IDebugVMConstants.PROP_FORMATTED_VALUE_ACTIVE_FORMAT.equals(property) || - IDebugVMConstants.PROP_FORMATTED_VALUE_FORMAT_PREFERENCE.equals(property) ) + if ( PROP_ACTIVE_FORMAT.equals(property) || + IDebugVMConstants.PROP_FORMATTED_VALUE_FORMAT_PREFERENCE.equals(property) ) { continue; } diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/numberformat/FormattedValueRetriever.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/numberformat/FormattedValueRetriever.java new file mode 100644 index 00000000000..f9f389c6885 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/numberformat/FormattedValueRetriever.java @@ -0,0 +1,608 @@ +/******************************************************************************* + * Copyright (c) 2009, 2010 Wind River 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.debug.ui.viewmodel.numberformat; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +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.debug.service.IFormattedValues; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMContext; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMData; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues.IFormattedDataDMContext; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.IDebugVMConstants; +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.cdt.dsf.service.DsfServices; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.dsf.ui.concurrent.ViewerDataRequestMonitor; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMNode; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext; +import org.eclipse.cdt.dsf.ui.viewmodel.properties.IPropertiesUpdate; +import org.eclipse.cdt.dsf.ui.viewmodel.properties.PropertiesUpdateStatus; +import org.eclipse.cdt.dsf.ui.viewmodel.update.ICacheEntry; +import org.eclipse.cdt.dsf.ui.viewmodel.update.ICachingVMProviderExtension2; +import org.eclipse.cdt.dsf.ui.viewmodel.update.IVMUpdatePolicyExtension; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.osgi.framework.Filter; +import org.osgi.framework.InvalidSyntaxException; +import org.osgi.util.tracker.ServiceTracker; + +/** + * A helper class for View Model Node implementations that support elements + * to be formatted using different number formats. This object can be + * instantiated by a VM node to retrieve formatted values from a given service + * using given DMC type. + * <p> + * Note: This class is a replacement for the {@link FormattedValueVMUtil#updateFormattedValues(IPropertiesUpdate[], IFormattedValues, Class, RequestMonitor)} + * static method. This new implementation retrieves cached values if they are + * available in the VM Cache. + * </p> + * + * @see FormattedValueVMUtil + * @see org.eclipse.cdt.dsf.ui.viewmodel.properties.IElementPropertiesProvider + * @see org.eclipse.cdt.dsf.debug.service.IFormattedValues + * + * @since 2.0 + */ +public class FormattedValueRetriever { + + private final IVMNode fNode; + private final ICachingVMProviderExtension2 fCache; + private final ServiceTracker fServiceTracker; + private final Class<? extends IFormattedDataDMContext> fDmcType; + private final String fPropertyPrefix; + + private final String PROP_AVAILABLE_FORMATS; + private final String PROP_ACTIVE_FORMAT; + private final String PROP_ACTIVE_FORMAT_VALUE; + private final String PROP_BASE; + + public FormattedValueRetriever(IVMNode node, DsfSession session, Class<?> serviceClass, Class<? extends IFormattedDataDMContext> dmcType) { + this(node, createFilter(session, serviceClass), dmcType, null); + } + + public FormattedValueRetriever(IVMNode node, DsfSession session, Class<?> serviceClass, Class<? extends IFormattedDataDMContext> dmcType, String propertyPrefix) { + this(node, createFilter(session, serviceClass), dmcType, propertyPrefix); + } + + public FormattedValueRetriever(IVMNode node, Filter filter, Class<? extends IFormattedDataDMContext> dmcType, String propertyPrefix) { + fNode = node; + fCache = (ICachingVMProviderExtension2)node.getVMProvider(); + fServiceTracker = new ServiceTracker(DsfUIPlugin.getBundleContext(), filter, null); + fServiceTracker.open(); + fDmcType = dmcType; + if (propertyPrefix == null) { + propertyPrefix = ""; //$NON-NLS-1$ + } + fPropertyPrefix = propertyPrefix; + PROP_AVAILABLE_FORMATS = (fPropertyPrefix + IDebugVMConstants.PROP_FORMATTED_VALUE_AVAILABLE_FORMATS).intern(); + PROP_ACTIVE_FORMAT = (fPropertyPrefix + IDebugVMConstants.PROP_FORMATTED_VALUE_ACTIVE_FORMAT).intern(); + PROP_ACTIVE_FORMAT_VALUE = (fPropertyPrefix + IDebugVMConstants.PROP_FORMATTED_VALUE_ACTIVE_FORMAT_VALUE).intern(); + PROP_BASE = (fPropertyPrefix + IDebugVMConstants.PROP_FORMATTED_VALUE_BASE).intern(); + } + + /** + * Creates an OSGI service filter for the given service type in a given + * DSF session. + */ + private static Filter createFilter(DsfSession session, Class<?> serviceClass) { + try { + return DsfUIPlugin.getBundleContext().createFilter( DsfServices.createServiceFilter(serviceClass, session.getId()) ); + } catch (InvalidSyntaxException e) { + throw new RuntimeException("Unable to create service filter for " + serviceClass, e); //$NON-NLS-1$ + } + } + + public void dispose() { + fServiceTracker.close(); + } + + /** + * This method fills in the formatted value properties in the given array + * of property update objects using data retrieved from the given + * formatted values service. + * <p> + * Note: The node parameter must return a <code>ICachingVMProviderExtension2</code> + * through its {@link IVMNode#getVMProvider()} method. + * + * @param node This method also takes an <code>IVMNode</code> parameter + * which allows for retrieving the format value data from the View Model + * cache. If the needed value property is cached already, the cached + * value will be used otherwise the properties will be retrieved from the + * service. + * + * @param updates The array of updates to fill in information to. This + * update is used to retrieve the data model context and to write the + * properties into. Implementation will not directly mark these updates + * complete, but contribute towards that end by marking [monitor] complete. + * + * @param service The service to be used to retrieve the values from. + * + * @param dmcType The class type of the data model context. Some updates + * can contain multiple formatted data data model contexts, and this + * method assures that there is no ambiguity in which context should be + * used. + * + * @param rm Request monitor used to signal completion of work + * + * @since 2.2 + */ + @ConfinedToDsfExecutor("node.getExecutor()") + public void update(final IPropertiesUpdate updates[], final RequestMonitor rm) + { + final Map<IPropertiesUpdate, String[]> cachedAvailableFormatsMap = calcCachedAvailableFormatsMap(updates); + if (cachedAvailableFormatsMap != null && cachedAvailableFormatsMap.size() == updates.length) { + // All updates were satisfied by the cache. + doUpdateWithAvailableFormats(updates, cachedAvailableFormatsMap, rm); + } else { + final IFormattedValues service = (IFormattedValues)fServiceTracker.getService(); + if (service == null) { + rm.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED, "Service not available " + fServiceTracker, null)); //$NON-NLS-1$ + rm.done(); + return; + } + + try { + service.getExecutor().execute(new DsfRunnable() { + public void run() { + retrieveAvailableFormats( + calcOutstandingAvailableFormatsUpdates(updates, cachedAvailableFormatsMap), + new DataRequestMonitor<Map<IPropertiesUpdate, String[]>>(fNode.getVMProvider().getExecutor(), rm) { + @Override + protected void handleSuccess() { + Map<IPropertiesUpdate, String[]> availableFormatsMap; + if (cachedAvailableFormatsMap != null) { + availableFormatsMap = cachedAvailableFormatsMap; + availableFormatsMap.putAll(getData()); + } else { + availableFormatsMap = getData(); + } + // Retrieve the formatted values now that we have the available formats (where needed). + // Note that we are passing off responsibility of our parent monitor + doUpdateWithAvailableFormats(updates, availableFormatsMap, rm); + } + }); + } + }); + } catch (RejectedExecutionException e) { + rm.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED, "Service executor shut down " + service.getExecutor(), e)); //$NON-NLS-1$ + rm.done(); + } + } + } + + /** + * Retrieves the <code>PROP_FORMATTED_VALUE_AVAILABLE_FORMATS</code> + * property for each update and returns it in a map. The returned + * map may be <code>null</code> if no cache data is available. + * + * @since 2.2 + */ + private Map<IPropertiesUpdate, String[]> calcCachedAvailableFormatsMap(IPropertiesUpdate updates[]) { + Map<IPropertiesUpdate, String[]> cachedAvailableFormatsMap = null; // delay creating map till needed + for (IPropertiesUpdate update : updates) { + ICacheEntry cacheEntry = fCache.getCacheEntry(fNode, update.getViewerInput(), update.getElementPath()); + if (cacheEntry != null && cacheEntry.getProperties() != null) { + String[] availableFormats = (String[]) + cacheEntry.getProperties().get(PROP_AVAILABLE_FORMATS); + // Add the cached entry to the cached map even if its null. This will help keep track + // of whether we need to call the service for data. + if (availableFormats != null || !isAvailableFormatsPropertyNeeded(update)) { + if (cachedAvailableFormatsMap == null) { + cachedAvailableFormatsMap = new HashMap<IPropertiesUpdate, String[]>(updates.length * 4/3); + } + cachedAvailableFormatsMap.put(update, availableFormats); + continue; + } + } + } + return cachedAvailableFormatsMap; + } + + /** + * Generates a list of updates which still need the + * <code>PROP_FORMATTED_VALUE_AVAILABLE_FORMATS</code> property. + * + * @since 2.2 + */ + private List<IPropertiesUpdate> calcOutstandingAvailableFormatsUpdates(IPropertiesUpdate[] updates, Map<IPropertiesUpdate, String[]> cachedAvailableFormatsMap) { + if (cachedAvailableFormatsMap != null) { + List<IPropertiesUpdate> outstandingUpdates = new ArrayList<IPropertiesUpdate>(updates.length - cachedAvailableFormatsMap.size()); + for (IPropertiesUpdate update : updates) { + if (!cachedAvailableFormatsMap.containsKey(update)) { + outstandingUpdates.add(update); + } + } + return outstandingUpdates; + } else { + return Arrays.asList(updates); + } + } + + /** + * Method to retrieve available formats for each update's element (if + * needed). The result is returned in a map and in the + * update object (if requested). + * <p> + * Note that we use a synchronized map because it's updated by a request + * monitor with an ImmediateExecutor. + * + * @since 2.2 + */ + @ConfinedToDsfExecutor("service.getExecutor()") + private void retrieveAvailableFormats( + final List<IPropertiesUpdate> updates, + final DataRequestMonitor<Map<IPropertiesUpdate, String[]>> rm) + { + IFormattedValues service = (IFormattedValues)fServiceTracker.getService(); + assert service.getExecutor().isInExecutorThread(); + + final Map<IPropertiesUpdate, String[]> availableFormats = Collections.synchronizedMap(new HashMap<IPropertiesUpdate, String[]>(updates.size() * 4/3)); + rm.setData(availableFormats); + final CountingRequestMonitor countingRm = new CountingRequestMonitor(service.getExecutor(), rm); + int count = 0; + + for (final IPropertiesUpdate update : updates) { + + if (!isAvailableFormatsPropertyNeeded(update)) { + continue; + } + + IFormattedDataDMContext dmc = getFormattedDataDMContext(update); + if (dmc == null) { + continue; + } + + service.getAvailableFormats( + dmc, + new ViewerDataRequestMonitor<String[]>(ImmediateExecutor.getInstance(), update) { + /** + * Note we don't mark the update object done, and we + * avoid calling our base implementation so that it + * doesn't either. The completion of this request is + * just a step in servicing the update. + */ + @Override + protected void handleCompleted() { + if (isSuccess()) { + // Set the result (available formats) into the update object if it was requested + if (update.getProperties().contains(PROP_AVAILABLE_FORMATS)) { + update.setProperty(PROP_AVAILABLE_FORMATS, getData()); + } + + if (getData().length != 0) { + // also add it to the map; we'll need to access it when querying the element's value. + availableFormats.put(update, getData()); + } else { + update.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED, "No number formats available for " + update.getElement(), null)); //$NON-NLS-1$ + } + } else { + update.setStatus(getStatus()); + } + countingRm.done(); + } + }); + count++; + } + countingRm.setDoneCount(count); + + } + + /** + * This method continues retrieving formatted value properties. It is + * called once the available formats are calculated for each requested + * update. + * + * @param availableFormatsMap Prior to calling this method, the caller + * queries (where necessary) the formats supported by the element in each + * update, and it puts that information in this map. If an entry in + * [updates] does not appear in this map, it means that its view-model + * element doesn't support any formats (very unlikely), or that the + * available formats aren't necessary to service the properties specified + * in the update + * + * @since 2.2 + */ + @ConfinedToDsfExecutor("fNode.getExecutor()") + private void doUpdateWithAvailableFormats( + IPropertiesUpdate updates[], + final Map<IPropertiesUpdate, String[]> availableFormatsMap, + final RequestMonitor rm) + { + final List<IPropertiesUpdate> outstandingUpdates = new ArrayList<IPropertiesUpdate>(updates.length); + final Map<IPropertiesUpdate, List<String>> requestedFormatsMap = new HashMap<IPropertiesUpdate, List<String>>(updates.length * 4 / 3); + final Map<IPropertiesUpdate, String> activeFormatsMap = new HashMap<IPropertiesUpdate, String>(updates.length * 4 / 3); + + for (final IPropertiesUpdate update : updates) { + String preferredFormat = FormattedValueVMUtil.getPreferredFormat(update.getPresentationContext()); + if (update.getProperties().contains(IDebugVMConstants.PROP_FORMATTED_VALUE_FORMAT_PREFERENCE)) { + update.setProperty(IDebugVMConstants.PROP_FORMATTED_VALUE_FORMAT_PREFERENCE, preferredFormat); + } + + final String activeFormat = calcActiveFormat(update, preferredFormat, availableFormatsMap); + + if (update.getProperties().contains(PROP_ACTIVE_FORMAT)) { + assert activeFormat != null : "Our caller should have provided the available formats if this property was specified; given available formats, an 'active' nomination is guaranteed."; //$NON-NLS-1$ + update.setProperty(PROP_ACTIVE_FORMAT, activeFormat); + } + List<String> requestedFormats = calcRequestedFormats(update, activeFormat, availableFormatsMap.get(update)); + + ICacheEntry cacheEntry = fCache.getCacheEntry(fNode, update.getViewerInput(), update.getElementPath()); + if (cacheEntry != null && cacheEntry.getProperties() != null) { + IVMUpdatePolicyExtension updatePolicy = getVMUpdatePolicyExtension(); + Iterator<String> itr = requestedFormats.iterator(); + while (itr.hasNext()) { + String format = itr.next(); + String formatProperty = FormattedValueVMUtil.getPropertyForFormatId(format, fPropertyPrefix); + Object value = cacheEntry.getProperties().get(formatProperty); + if (value != null || !canUpdateProperty(cacheEntry, updatePolicy, formatProperty)) { + itr.remove(); + setUpdateFormatProperty(update, activeFormat, format, value); + } + } + } + + if (!requestedFormats.isEmpty()) { + outstandingUpdates.add(update); + requestedFormatsMap.put(update, requestedFormats); + activeFormatsMap.put(update, activeFormat); + } + } + final IFormattedValues service = (IFormattedValues)fServiceTracker.getService(); + if (service == null) { + rm.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED, "Service not available " + fServiceTracker, null)); //$NON-NLS-1$ + rm.done(); + return; + } + try { + service.getExecutor().execute(new DsfRunnable() { + public void run() { + doUpdateWithRequestedFormats(outstandingUpdates, requestedFormatsMap, activeFormatsMap, rm); + } + }); + } catch (RejectedExecutionException e) { + rm.setStatus(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.REQUEST_FAILED, "Service executor shut down " + service.getExecutor(), e)); //$NON-NLS-1$ + rm.done(); + } + } + + private IVMUpdatePolicyExtension getVMUpdatePolicyExtension() { + if( fCache.getActiveUpdatePolicy() instanceof IVMUpdatePolicyExtension) { + return (IVMUpdatePolicyExtension)fCache.getActiveUpdatePolicy(); + } + return null; + } + + private static boolean canUpdateProperty(ICacheEntry entry, IVMUpdatePolicyExtension updatePolicy, String property) { + return !entry.isDirty() || (updatePolicy != null && updatePolicy.canUpdateDirtyProperty(entry, property)); + } + + /** + * Retrieves the specified formatted values from the service. + * + * @param requestedFormatsMap Map containing the formats to be retrieved + * and filled in for each given update. + * @param activeFormatsMap Map containing the active format for each given + * update. The active format value needs to be set in the update using the + * special property <code>PROP_FORMATTED_VALUE_ACTIVE_FORMAT_VALUE</code>. + * + * @since 2.2 + */ + @ConfinedToDsfExecutor("service.getExecutor()") + private void doUpdateWithRequestedFormats( + List<IPropertiesUpdate> updates, + final Map<IPropertiesUpdate, List<String>> requestedFormatsMap, + final Map<IPropertiesUpdate, String> activeFormatsMap, + final RequestMonitor monitor) + { + IFormattedValues service = (IFormattedValues)fServiceTracker.getService(); + assert service.getExecutor().isInExecutorThread(); + + // Use a single counting RM for all the requested formats for each update. + final CountingRequestMonitor countingRm = new CountingRequestMonitor(ImmediateExecutor.getInstance(), monitor); + int count = 0; + + for (final IPropertiesUpdate update : updates) { + IFormattedDataDMContext dmc = getFormattedDataDMContext(update); + if (dmc == null) { + continue; + } + + List<String> requestedFormats = requestedFormatsMap.get(update); + for (String requestedFormat : requestedFormats) { + final FormattedValueDMContext formattedValueDmc = service.getFormattedValueContext(dmc, requestedFormat); + service.getFormattedExpressionValue( + formattedValueDmc, + // Here also use the ViewerDataRequestMonitor in order to propagate the update's cancel request. + // However, when operation is complete, call the counting RM's done(). + // Use an immediate executor to avoid the possibility of a rejected execution exception. + new ViewerDataRequestMonitor<FormattedValueDMData>(ImmediateExecutor.getInstance(), update) { + @Override + protected void handleCompleted() { + if (isSuccess()) { + setUpdateFormatProperty( + update, + activeFormatsMap.get(update), + formattedValueDmc.getFormatID(), + getData().getFormattedValue()); + } else { + update.setStatus(getStatus()); + } + // Note: we must not call the update's done method, instead call counting RM done. + countingRm.done(); + + }; + }); + count++; + } + } + countingRm.setDoneCount(count); + } + + /** + * Determine the 'active' value format. It is the view preference if + * and only if the element supports it. Otherwise it is the first + * format supported by the element. + * <p> + * Note: If the availableFormatsMap doesn't contain the available formats + * for the given update, it means the update doesn't request any properties + * which requires the active format to be calculated. + * + * @param update Properties update to calculate the active format for. + * @param availableFormatsMap The map of available formats. + * @return The active format, or null if active format not requested in + * update. + */ + private String calcActiveFormat(IPropertiesUpdate update, String preferredFormat, Map<IPropertiesUpdate, String[]> availableFormatsMap) { + String[] availableFormats = availableFormatsMap.get(update); + if (availableFormats != null && availableFormats.length != 0) { + if (isFormatAvailable(preferredFormat, availableFormats)) { + return preferredFormat; + } else { + return availableFormats[0]; + } + } + return null; // null means we don't need to know what the active format is + } + + /** + * Returns <code>true</code> if the given availableFormats array contains + * the given format. + */ + private boolean isFormatAvailable(String format, String[] availableFormats) { + for (String availableFormat : availableFormats) { + if (availableFormat.equals(format)) { + return true; + } + } + return false; + } + + /** + * Service the properties that ask for the value in a specific + * format. If the update request contains the property + * PROP_FORMATTED_VALUE_ACTIVE_FORMAT_VALUE, and the active format + * has not been explicitly requested, then we need an additional + * iteration to provide it. + */ + private List<String> calcRequestedFormats(IPropertiesUpdate update, String activeFormat, String[] availableFormats) { + List<String> requestedFormats = new ArrayList<String>(10); + + boolean activeFormatValueHandled = false; // have we come across a specific format request that is the active format? + + for (Iterator<String> itr = update.getProperties().iterator(); itr.hasNext() || (activeFormat != null && !activeFormatValueHandled);) { + String nextFormat; + if (itr.hasNext()) { + String propertyName = itr.next(); + if (propertyName.startsWith(PROP_BASE)) { + nextFormat = FormattedValueVMUtil.getFormatFromProperty(propertyName, fPropertyPrefix); + if (nextFormat.equals(activeFormat)) { + activeFormatValueHandled = true; + } + // if we know the supported formats (we may not), then no-op if this format is unsupported + if (availableFormats != null && !isFormatAvailable(nextFormat, availableFormats)) { + continue; + } + } + else { + continue; + } + } else { + // the additional iteration to handle the active format + nextFormat = activeFormat; + activeFormatValueHandled = true; + } + requestedFormats.add(nextFormat); + } + return requestedFormats; + } + + /** + * Writes the given formatted property value into the update. It also + * writes the active format property if needed. + * <p> + * If the given property value is null, this method writes an error status + * instead. + */ + private void setUpdateFormatProperty(IPropertiesUpdate update, String activeFormat, String format, Object value) { + String formatProperty = FormattedValueVMUtil.getPropertyForFormatId(format, fPropertyPrefix); + if (value != null) { + update.setProperty(formatProperty, value); + if (update.getProperties().contains(PROP_ACTIVE_FORMAT_VALUE) && + format.equals(activeFormat)) + { + update.setProperty(PROP_ACTIVE_FORMAT_VALUE, value); + } + } else { + IStatus staleDataStatus = DsfUIPlugin.newErrorStatus(IDsfStatusConstants.INVALID_STATE, "Cache contains stale data. Refresh view.", null );//$NON-NLS-1$ + if (update.getProperties().contains(PROP_ACTIVE_FORMAT_VALUE) && + format.equals(activeFormat)) + { + PropertiesUpdateStatus.getPropertiesStatus(update).setStatus( + new String[] { PROP_ACTIVE_FORMAT_VALUE, formatProperty }, + staleDataStatus); + } else { + PropertiesUpdateStatus.getPropertiesStatus(update).setStatus(formatProperty, staleDataStatus); + } + } + } + + /** + * For each update, query the formats available for the update's + * element...but only if necessary. The available formats are necessary + * only if the update explicitly requests that information, or if the + * update is asking what the active format is or is asking for the value + * of the element in that format. The reason we need them in the last + * two cases is that we can't establish the 'active' format for an + * element without knowing its available formats. See + * updateFormattedValuesWithAvailableFormats(), as that's where we make + * that determination. + * @param update + * @return + */ + private boolean isAvailableFormatsPropertyNeeded(IPropertiesUpdate update) { + return update.getProperties().contains(PROP_AVAILABLE_FORMATS) || + update.getProperties().contains(PROP_ACTIVE_FORMAT) || + update.getProperties().contains(PROP_ACTIVE_FORMAT_VALUE); + } + + /** + * Extracts the formatted data DMC from the update. If update doesn't + * contain DMC-based elemtn, it writes an error to the update and returns + * <code>null</code>. + */ + private IFormattedDataDMContext getFormattedDataDMContext(IPropertiesUpdate update) + { + IFormattedDataDMContext dmc = null; + if (update.getElement() instanceof IDMVMContext) { + dmc = DMContexts.getAncestorOfType(((IDMVMContext)update.getElement()).getDMContext(), fDmcType); + } + + if (dmc == null) { + update.setStatus(DsfUIPlugin.newErrorStatus(IDsfStatusConstants.INVALID_HANDLE, "Update element did not contain a valid context: " + fDmcType, null)); //$NON-NLS-1$ + } + return dmc; + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/numberformat/FormattedValueVMUtil.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/numberformat/FormattedValueVMUtil.java index 62a369a6927..187ffff3ff1 100644 --- a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/numberformat/FormattedValueVMUtil.java +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/numberformat/FormattedValueVMUtil.java @@ -10,12 +10,11 @@ *******************************************************************************/ package org.eclipse.cdt.dsf.debug.ui.viewmodel.numberformat; -import com.ibm.icu.text.MessageFormat; - import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.Map; +import java.util.TreeMap; import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor; import org.eclipse.cdt.dsf.concurrent.CountingRequestMonitor; @@ -34,6 +33,8 @@ import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext; import org.eclipse.cdt.dsf.ui.viewmodel.properties.IPropertiesUpdate; import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import com.ibm.icu.text.MessageFormat; + /** * A helper class for View Model Node implementations that support elements * to be formatted using different number formats. The various static methods in @@ -48,6 +49,12 @@ import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationCont public class FormattedValueVMUtil { /** + * Cache to avoid creating many duplicate strings of formats and properties. + */ + private static Map<String, Map<String, String>> fFormatProperties = + Collections.synchronizedMap(new TreeMap<String, Map<String, String>>()); + + /** * Common map of user-readable labels for format IDs. */ private static Map<String, String> fFormatLabels = new HashMap<String, String>(8); @@ -66,7 +73,7 @@ public class FormattedValueVMUtil { * add its label to the map of format IDs using this method. * * @param formatId Format ID to set the label for. - * @param label User-readable lable for a format. + * @param label User-readable label for a format. */ public static void setFormatLabel(String formatId, String label) { fFormatLabels.put(formatId, label); @@ -87,20 +94,102 @@ public class FormattedValueVMUtil { /** * Returns an element property representing an element value in a given format. + + * @deprecated Replaced by {@link #getPropertyForFormatId(String, String)} */ public static String getPropertyForFormatId(String formatId) { + return getPropertyForFormatId(formatId, ""); //$NON-NLS-1$ + } + + /** + * Returns an element property representing an element value in a given format. + * + * @param Format ID to create the property for. + * @param prefix The prefix for the property that is used to distinguish + * it from other number format values in a given property map. May be + * <code>null</code> or an empty string if no prefix is used. + * @return The generated property name. + * + * @since 2.2 + */ + public static String getPropertyForFormatId(String formatId, String prefix) { if (formatId == null) { return null; } - return IDebugVMConstants.PROP_FORMATTED_VALUE_BASE + "." + formatId; //$NON-NLS-1$ + if (prefix == null) { + prefix = ""; //$NON-NLS-1$ + } + synchronized(fFormatProperties) { + Map<String, String> formatsMap = getFormatsMap(prefix); + String property = formatsMap.get(formatId); + if (property == null) { + property = (prefix + IDebugVMConstants.PROP_FORMATTED_VALUE_BASE + "." + formatId).intern(); //$NON-NLS-1$ + formatsMap.put(formatId, property); + } + return property; + } } + private static Map<String, String> getFormatsMap(String prefix) { + synchronized(fFormatProperties) { + Map<String, String> prefixMap = fFormatProperties.get(prefix); + if (prefixMap == null) { + prefixMap = new TreeMap<String, String>(); + fFormatProperties.put(prefix, prefixMap); + } + return prefixMap; + } + } + /** * Returns a format ID based on the element property representing a - * formatted element value. + * formatted element value. + * + * @deprecated Replaced by {@link #getFormatFromProperty(String, String)} */ public static String getFormatFromProperty(String property) { - return property.substring(IDebugVMConstants.PROP_FORMATTED_VALUE_BASE.length() + 1); + return getFormatFromProperty(property, ""); //$NON-NLS-1$ + } + + /** + * Returns a format ID based on the element property representing a + * formatted element value. This method has an additional prefix parameter + * which is used when multiple number formats are stored in a single + * property map. + * + * @param property The property to extract the format from. + * @param prefix The prefix for the property that is used to distinguish + * it from other number format values in a given property map. May be + * <code>null</code> or an empty string if no prefix is used. + * @return The format ID. + * + * @throws IllegalArgumentException if the property is not a formatted value + * property. + * + * @since 2.2 + */ + public static String getFormatFromProperty(String property, String prefix) { + if (prefix == null) { + prefix = ""; //$NON-NLS-1$ + } + + synchronized(fFormatProperties) { + Map<String, String> formatsMap = getFormatsMap(prefix); + for (Map.Entry<String, String> entry : formatsMap.entrySet()) { + if (entry.getValue().equals(property)) { + return entry.getKey(); + } + } + if ( !property.startsWith(prefix) || + !property.startsWith(IDebugVMConstants.PROP_FORMATTED_VALUE_BASE, prefix.length()) ) + { + throw new IllegalArgumentException("Property " + property + " is not a valid formatted value format property."); //$NON-NLS-1$//$NON-NLS-2$ + } + String formatId = property.substring( + prefix.length() + IDebugVMConstants.PROP_FORMATTED_VALUE_BASE.length() + 1).intern(); + formatsMap.put(formatId, property); + return formatId; + } } @@ -116,6 +205,7 @@ public class FormattedValueVMUtil { return IFormattedValues.NATURAL_FORMAT; } + /** * This method fills in the formatted value properties in the given array * of property update objects using data retrieved from the given @@ -131,6 +221,9 @@ public class FormattedValueVMUtil { * method assures that there is no ambiguity in which context should be * used. * @param monitor Request monitor used to signal completion of work + * + * @deprecated This method has been replaced by the {@link FormattedValueRetriever} + * utility. */ @ConfinedToDsfExecutor("service.getExecutor()") public static void updateFormattedValues( diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/register/RegisterBitFieldVMNode.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/register/RegisterBitFieldVMNode.java index 45b39fb01c0..2cab98e7505 100644 --- a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/register/RegisterBitFieldVMNode.java +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/register/RegisterBitFieldVMNode.java @@ -22,8 +22,8 @@ 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.IRegisters; 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.IBitFieldChangedDMEvent; import org.eclipse.cdt.dsf.debug.service.IRegisters.IBitFieldDMContext; import org.eclipse.cdt.dsf.debug.service.IRegisters.IBitFieldDMData; @@ -33,11 +33,12 @@ 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.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.FormattedValueVMUtil; +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.register.RegisterBitFieldCellModifier.BitFieldEditorStyle; import org.eclipse.cdt.dsf.debug.ui.viewmodel.variable.VariableLabelFont; @@ -56,6 +57,7 @@ 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; @@ -83,7 +85,6 @@ import org.eclipse.jface.viewers.ComboBoxCellEditor; import org.eclipse.jface.viewers.ICellModifier; import org.eclipse.jface.viewers.TextCellEditor; import org.eclipse.jface.viewers.TreePath; -import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.widgets.Composite; public class RegisterBitFieldVMNode extends AbstractExpressionVMNode @@ -173,12 +174,25 @@ public class RegisterBitFieldVMNode extends AbstractExpressionVMNode */ private IElementLabelProvider fLabelProvider; + /** + * Retriever for formatted values configured for this VM node. + * @since 2.2 + */ + private final FormattedValueRetriever fFormattedValueRetriever; + public RegisterBitFieldVMNode(AbstractDMVMProvider provider, DsfSession session, SyncRegisterDataAccess access) { super(provider, session, IBitFieldDMContext.class); fSyncRegisterDataAccess = access; fLabelProvider = createLabelProvider(); + fFormattedValueRetriever = + new FormattedValueRetriever(this, session, IRegisters.class, IBitFieldDMContext.class); + } + + @Override + public void dispose() { + super.dispose(); + fFormattedValueRetriever.dispose(); } - @Override public String toString() { @@ -284,15 +298,7 @@ public class RegisterBitFieldVMNode extends AbstractExpressionVMNode IRegisterVMConstants.PROP_CURRENT_MNEMONIC_LONG_NAME}), new FormattedValueLabelText(), new ErrorLabelText(), - new LabelForeground(new RGB(255, 0, 0)) // TODO: replace with preference error color - { - { setPropertyNames(new String[] { PROP_NAME }); } - - @Override - public boolean isEnabled(IStatus status, java.util.Map<String,Object> properties) { - return !status.isOK(); - } - }, + new ErrorLabelForeground(), new LabelBackground( DebugUITools.getPreferenceColor(IDebugUIConstants.PREF_CHANGED_VALUE_BACKGROUND).getRGB()) { @@ -493,23 +499,51 @@ public class RegisterBitFieldVMNode extends AbstractExpressionVMNode } /** + * 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 */ 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() { public void run() { - updatePropertiesInSessionThread(updates); + updatePropertiesInSessionThread(subUpdates); }}); } catch (RejectedExecutionException e) { - for (IPropertiesUpdate update : updates) { - handleFailedUpdate(update); + 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 @@ -541,11 +575,6 @@ public class RegisterBitFieldVMNode extends AbstractExpressionVMNode }; int count = 0; - if (service != null) { - FormattedValueVMUtil.updateFormattedValues(updates, service, IBitFieldDMContext.class, countingRm); - count++; - } - for (final IPropertiesUpdate update : updates) { IExpression expression = (IExpression)DebugPlugin.getAdapter(update.getElement(), IExpression.class); if (expression != null) { diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/register/RegisterVMNode.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/register/RegisterVMNode.java index 56379ffb872..82ac014133e 100644 --- a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/register/RegisterVMNode.java +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/register/RegisterVMNode.java @@ -22,19 +22,20 @@ 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.IRegisters; 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.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.FormattedValueVMUtil; +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.variable.VariableLabelFont; import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; @@ -52,6 +53,7 @@ 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; @@ -80,7 +82,6 @@ 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.graphics.RGB; import org.eclipse.swt.widgets.Composite; /** @@ -171,10 +172,18 @@ public class RegisterVMNode extends AbstractExpressionVMNode */ 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 ) { @@ -218,7 +227,9 @@ public class RegisterVMNode extends AbstractExpressionVMNode DebugUITools.getPreferenceStore().removePropertyChangeListener(fPreferenceChangeListener); } - super.dispose(); + super.dispose(); + + fFormattedValueRetriever.dispose(); } protected IElementLabelProvider createLabelProvider() { @@ -325,15 +336,7 @@ public class RegisterVMNode extends AbstractExpressionVMNode new LabelColumnInfo(new LabelAttribute[] { new FormattedValueLabelText(), new ErrorLabelText(), - new LabelForeground(new RGB(255, 0, 0)) // TODO: replace with preference error color - { - { setPropertyNames(new String[] { PROP_NAME }); } - - @Override - public boolean isEnabled(IStatus status, java.util.Map<String,Object> properties) { - return !status.isOK(); - } - }, + new ErrorLabelForeground(), columnIdValueBackground, new StaleDataLabelForeground(), new VariableLabelFont(), @@ -460,19 +463,46 @@ public class RegisterVMNode extends AbstractExpressionVMNode } /** + * 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 */ 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() { public void run() { - updatePropertiesInSessionThread(updates); + updatePropertiesInSessionThread(subUpdates); }}); } catch (RejectedExecutionException e) { - for (IPropertiesUpdate update : updates) { - handleFailedUpdate(update); + 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(); } } } @@ -513,11 +543,6 @@ public class RegisterVMNode extends AbstractExpressionVMNode }; int count = 0; - if (service != null) { - FormattedValueVMUtil.updateFormattedValues(updates, service, IRegisterDMContext.class, countingRm); - count++; - } - for (final IPropertiesUpdate update : updates) { IExpression expression = (IExpression)DebugPlugin.getAdapter(update.getElement(), IExpression.class); if (expression != null) { diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/update/DebugManualUpdatePolicy.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/update/DebugManualUpdatePolicy.java index 107d4519119..ef16720e353 100644 --- a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/update/DebugManualUpdatePolicy.java +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/update/DebugManualUpdatePolicy.java @@ -10,13 +10,17 @@ *******************************************************************************/ package org.eclipse.cdt.dsf.debug.ui.viewmodel.update; -import java.util.ArrayList; import java.util.Collection; -import java.util.List; +import java.util.Set; +import java.util.TreeSet; import org.eclipse.cdt.dsf.debug.ui.viewmodel.IDebugVMConstants; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.numberformat.FormattedValueRetriever; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.numberformat.FormattedValueVMUtil; +import org.eclipse.cdt.dsf.ui.viewmodel.update.ICacheEntry; import org.eclipse.cdt.dsf.ui.viewmodel.update.IElementUpdateTester; import org.eclipse.cdt.dsf.ui.viewmodel.update.IElementUpdateTesterExtension; +import org.eclipse.cdt.dsf.ui.viewmodel.update.IVMUpdatePolicyExtension; import org.eclipse.cdt.dsf.ui.viewmodel.update.ManualUpdatePolicy; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.jface.viewers.TreePath; @@ -24,17 +28,54 @@ import org.eclipse.jface.viewers.TreePath; /** * Manual update policy with extensions specific for the debugger views. It * properly handles the changes in active number format values in debug view. + * This requires clearing of cached properties related to the active format + * preference, but not clearing the formatted value data retrieved from the + * service. * * @since 2.1 */ -public class DebugManualUpdatePolicy extends ManualUpdatePolicy { +public class DebugManualUpdatePolicy extends ManualUpdatePolicy implements IVMUpdatePolicyExtension { - public static String DEBUG_MANUAL_UPDATE_POLICY_ID = "org.eclipse.cdt.dsf.debug.ui.viewmodel.update.debugManualUpdatePolicy"; //$NON-NLS-1$ + private final Set<String> fActiveNumberFormatPropertiesWithPrefixes; - private static final List<String> ACTIVE_NUMBER_FORMAT_PROPERTIES = new ArrayList<String>(1); + /** + * Creates a manual update policy for debug views. + */ + public DebugManualUpdatePolicy() { + this(new String[0]); + } + + /** + * Creates a manual update policy for debug views for models that retrieve + * multiple formatted values for each view entry. The given prefixes + * distinguish the formatted values properties from each other. + * + * @see FormattedValueRetriever + * @see FormattedValueVMUtil#getPropertyForFormatId(String, String) + * + * @param prefixes Prefixes to use when flushing the active formatted value + * from VM cache. + */ + public DebugManualUpdatePolicy(String[] prefixes) { + if (prefixes.length == 0) { + fActiveNumberFormatPropertiesWithPrefixes = ACTIVE_NUMBER_FORMAT_PROPERTIES; + } else { + fActiveNumberFormatPropertiesWithPrefixes = new TreeSet<String>(ACTIVE_NUMBER_FORMAT_PROPERTIES); + for (String prefix : prefixes) { + fActiveNumberFormatPropertiesWithPrefixes.add( + (prefix + IDebugVMConstants.PROP_FORMATTED_VALUE_ACTIVE_FORMAT).intern()); + fActiveNumberFormatPropertiesWithPrefixes.add( + (prefix + IDebugVMConstants.PROP_FORMATTED_VALUE_ACTIVE_FORMAT_VALUE).intern()); + } + } + + } + private static final Set<String> ACTIVE_NUMBER_FORMAT_PROPERTIES = new TreeSet<String>(); static { ACTIVE_NUMBER_FORMAT_PROPERTIES.add(IDebugVMConstants.PROP_FORMATTED_VALUE_ACTIVE_FORMAT); + ACTIVE_NUMBER_FORMAT_PROPERTIES.add(IDebugVMConstants.PROP_FORMATTED_VALUE_ACTIVE_FORMAT_VALUE); + ACTIVE_NUMBER_FORMAT_PROPERTIES.add(IDebugVMConstants.PROP_FORMATTED_VALUE_FORMAT_PREFERENCE); } /** @@ -42,17 +83,14 @@ public class DebugManualUpdatePolicy extends ManualUpdatePolicy { * property of the elemetn under consideration. The partial property flush * is performed only if the cache entry is not yet dirty. */ - private static IElementUpdateTester fgNumberFormatPropertyEventUpdateTester = new IElementUpdateTesterExtension() { + private IElementUpdateTester fNumberFormatPropertyEventUpdateTester = new IElementUpdateTesterExtension() { public int getUpdateFlags(Object viewerInput, TreePath path) { return FLUSH_PARTIAL_PROPERTIES; } public Collection<String> getPropertiesToFlush(Object viewerInput, TreePath path, boolean isDirty) { - if (!isDirty) { - return ACTIVE_NUMBER_FORMAT_PROPERTIES; - } - return null; + return fActiveNumberFormatPropertiesWithPrefixes; } public boolean includes(IElementUpdateTester tester) { @@ -66,17 +104,16 @@ public class DebugManualUpdatePolicy extends ManualUpdatePolicy { }; @Override - public String getID() { - return DEBUG_MANUAL_UPDATE_POLICY_ID; - } - - @Override public IElementUpdateTester getElementUpdateTester(Object event) { - if ((event instanceof PropertyChangeEvent && - ((PropertyChangeEvent)event).getProperty() == IDebugVMConstants.PROP_FORMATTED_VALUE_FORMAT_PREFERENCE)) + if ( event instanceof PropertyChangeEvent && + IDebugVMConstants.PROP_FORMATTED_VALUE_FORMAT_PREFERENCE.equals( ((PropertyChangeEvent)event).getProperty()) ) { - return fgNumberFormatPropertyEventUpdateTester; + return fNumberFormatPropertyEventUpdateTester; } return super.getElementUpdateTester(event); } + + public boolean canUpdateDirtyProperty(ICacheEntry entry, String property) { + return fActiveNumberFormatPropertiesWithPrefixes.contains(property); + } } diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/variable/VariableVMNode.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/variable/VariableVMNode.java index af5da3bb96c..fb4b5339867 100644 --- a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/variable/VariableVMNode.java +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/debug/ui/viewmodel/variable/VariableVMNode.java @@ -47,11 +47,13 @@ import org.eclipse.cdt.dsf.debug.service.IStack; import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext; import org.eclipse.cdt.dsf.debug.service.IStack.IVariableDMContext; import org.eclipse.cdt.dsf.debug.service.IStack.IVariableDMData; +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.expression.IExpressionUpdate; 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.FormattedValueVMUtil; import org.eclipse.cdt.dsf.debug.ui.viewmodel.numberformat.IFormattedValueVMContext; import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; @@ -70,6 +72,7 @@ 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; @@ -100,7 +103,6 @@ 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.graphics.RGB; import org.eclipse.swt.widgets.Composite; import org.eclipse.ui.IMemento; @@ -143,6 +145,12 @@ public class VariableVMNode extends AbstractExpressionVMNode */ private final IElementLabelProvider fLabelProvider; + /** + * Retriever for formatted values configured for this VM node. + * @since 2.2 + */ + private final FormattedValueRetriever fFormattedValueRetriever; + public class VariableExpressionVMC extends DMVMContext implements IFormattedValueVMContext { private IExpression fExpression; @@ -227,6 +235,8 @@ public class VariableVMNode extends AbstractExpressionVMNode super(provider, session, IExpressions.IExpressionDMContext.class); fSyncVariableDataAccess = syncVariableDataAccess; fLabelProvider = createLabelProvider(); + fFormattedValueRetriever = + new FormattedValueRetriever(this, session, IExpressions.class, IExpressionDMContext.class); } /** @@ -263,7 +273,10 @@ public class VariableVMNode extends AbstractExpressionVMNode DebugUITools.getPreferenceStore().removePropertyChangeListener(fPreferenceChangeListener); } - super.dispose(); + + super.dispose(); + + fFormattedValueRetriever.dispose(); } /** @@ -459,16 +472,7 @@ public class VariableVMNode extends AbstractExpressionVMNode }, new FormattedValueLabelText(), new ErrorLabelText(), - new LabelForeground(new RGB(255, 0, 0)) // TODO: replace with preference error color - { - { setPropertyNames(new String[] { PROP_NAME }); } - - @Override - public boolean isEnabled(IStatus status, java.util.Map<String,Object> properties) { - return !status.isOK(); - } - }, - // + new ErrorLabelForeground(), columnIdValueBackground, new StaleDataLabelForeground(), new VariableLabelFont(), @@ -607,15 +611,7 @@ public class VariableVMNode extends AbstractExpressionVMNode POINTER_LABEL_IMAGE, AGGREGATE_LABEL_IMAGE, SIMPLE_LABEL_IMAGE, - new LabelForeground(new RGB(255, 0, 0)) // TODO: replace with preference error color - { - { setPropertyNames(new String[] { PROP_NAME }); } - - @Override - public boolean isEnabled(IStatus status, java.util.Map<String,Object> properties) { - return !status.isOK(); - } - }, + new ErrorLabelForeground(), new LabelForeground( DebugUITools.getPreferenceColor(IDebugUIConstants.PREF_CHANGED_DEBUG_ELEMENT_COLOR).getRGB()) { @@ -659,19 +655,46 @@ public class VariableVMNode extends AbstractExpressionVMNode } /** + * 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 */ 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() { public void run() { - updatePropertiesInSessionThread(updates); + updatePropertiesInSessionThread(subUpdates); }}); } catch (RejectedExecutionException e) { - for (IPropertiesUpdate update : updates) { - handleFailedUpdate(update); + 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(); } } } @@ -707,11 +730,6 @@ public class VariableVMNode extends AbstractExpressionVMNode }; int count = 0; - if (service != null) { - FormattedValueVMUtil.updateFormattedValues(updates, service, IExpressionDMContext.class, countingRm); - count++; - } - for (final IPropertiesUpdate update : updates) { IExpression expression = (IExpression)DebugPlugin.getAdapter(update.getElement(), IExpression.class); if (expression != null) { diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/IPropertiesUpdate.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/IPropertiesUpdate.java index 84a966e882d..24220703169 100644 --- a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/IPropertiesUpdate.java +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/IPropertiesUpdate.java @@ -36,6 +36,9 @@ public interface IPropertiesUpdate extends IViewerUpdate { /** * Sets the given map as the complete property map for this update. + * If other properties were already set to this update, the properties + * given here will be added. If properties are added later, the properties + * map given here will not be modified, instead it will be copied. * * @param properties Full properties map. */ diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/IPropertiesUpdateListener.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/IPropertiesUpdateListener.java new file mode 100644 index 00000000000..13f5a02f83f --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/IPropertiesUpdateListener.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2010 Wind River 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.ui.viewmodel.properties; + +/** + * Listener for properties updates requested by a property based label provider. + * + * @since 2.2 + */ +public interface IPropertiesUpdateListener { + + /** + * Indicates that the given updates were requested from a properties provider. + */ + public void propertiesUpdatesStarted(IPropertiesUpdate[] updates); + + /** + * Indicates that the given update has been completed. + */ + public void propertiesUpdateCompleted(IPropertiesUpdate update); + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/MessagesForProperties.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/MessagesForProperties.java index 7d6c400dcbf..0527b3f487e 100644 --- a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/MessagesForProperties.java +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/MessagesForProperties.java @@ -17,6 +17,7 @@ import org.eclipse.osgi.util.NLS; */ class MessagesForProperties extends NLS { public static String DefaultLabelMessage_label; + public static String PropertiesUpdateStatus_message; static { // initialize resource bundle diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/MessagesForProperties.properties b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/MessagesForProperties.properties index cdf03432bfc..a00405819bf 100644 --- a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/MessagesForProperties.properties +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/MessagesForProperties.properties @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2006, 2009 Wind River Systems and others. +# Copyright (c) 2006, 2010 Wind River 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 @@ -9,3 +9,4 @@ # Wind River Systems - initial API and implementation ############################################################################### DefaultLabelMessage_label=<unknown> +PropertiesUpdateStatus_message=Multiple errors reported.
\ No newline at end of file diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/PropertiesBasedLabelProvider.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/PropertiesBasedLabelProvider.java index 8a720ee7609..d0bb2df7bc3 100644 --- a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/PropertiesBasedLabelProvider.java +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/PropertiesBasedLabelProvider.java @@ -10,6 +10,7 @@ *******************************************************************************/ package org.eclipse.cdt.dsf.ui.viewmodel.properties; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -49,6 +50,8 @@ public class PropertiesBasedLabelProvider */ private Map<String, LabelColumnInfo> fColumnInfos = Collections.synchronizedMap(new HashMap<String,LabelColumnInfo>()); + private IPropertiesUpdateListener[] fListeners = new IPropertiesUpdateListener[0]; + /** * Standard constructor. A property based label constructor does not * initialize column attribute information {@link #setColumnInfo(String, LabelColumnInfo)} @@ -81,6 +84,40 @@ public class PropertiesBasedLabelProvider return fColumnInfos.get(columnId); } + /** + * Adds a listener for properties updates generated by this label provider. + * + * @since 2.2 + */ + public void addPropertiesUpdateListener(IPropertiesUpdateListener listener) { + synchronized(this) { + if (!Arrays.asList(fListeners).contains(listener)) { + IPropertiesUpdateListener[] newListeners = new IPropertiesUpdateListener[fListeners.length + 1]; + System.arraycopy(fListeners, 0, newListeners, 0, fListeners.length); + newListeners[fListeners.length] = listener; + fListeners = newListeners; + } + } + } + + /** + * Removes a listener for properties updates generated by this label provider. + * + * @since 2.2 + */ + public void removePropertiesUpdateListener(IPropertiesUpdateListener listener) { + synchronized(this) { + int listenerIdx = Arrays.asList(fListeners).indexOf(listener); + + if (listenerIdx != -1) { + IPropertiesUpdateListener[] newListeners = new IPropertiesUpdateListener[fListeners.length - 1]; + System.arraycopy(fListeners, 0, newListeners, 0, listenerIdx); + System.arraycopy(fListeners, listenerIdx + 1, newListeners, listenerIdx, newListeners.length - listenerIdx); + fListeners = newListeners; + } + } + } + /** * In addition to guarantees on [labelUpdates] declared by * {@link IElementLabelProvider}, we further require/assume that all the @@ -89,7 +126,7 @@ public class PropertiesBasedLabelProvider * * @see org.eclipse.debug.internal.ui.viewers.model.provisional.IElementLabelProvider#update(org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate[]) */ - public void update(ILabelUpdate[] labelUpdates) { + public void update(final ILabelUpdate[] labelUpdates) { IElementPropertiesProvider propertiesProvider = getElementPropertiesProvider(labelUpdates[0].getElement()); if (propertiesProvider == null) { for (ILabelUpdate update : labelUpdates) { @@ -109,19 +146,41 @@ public class PropertiesBasedLabelProvider // Call the properties provider. Create a request monitor for each label update. // We can use an immediate executor for the request monitor because the label provider // is thread safe. - IPropertiesUpdate[] propertiesUpdates = new IPropertiesUpdate[labelUpdates.length]; + final IPropertiesUpdate[] propertiesUpdates = new IPropertiesUpdate[labelUpdates.length]; for (int i = 0; i < labelUpdates.length; i++) { - final ILabelUpdate labelUpdate = labelUpdates[i]; - propertiesUpdates[i] = new VMPropertiesUpdate( - propertyNames, labelUpdate, - new ViewerDataRequestMonitor<Map<String, Object>>(ImmediateExecutor.getInstance(), labelUpdate) { + final int idx = i; + propertiesUpdates[idx] = new VMPropertiesUpdate( + propertyNames, labelUpdates[idx], + new ViewerDataRequestMonitor<Map<String, Object>>(ImmediateExecutor.getInstance(), labelUpdates[idx]) { @Override protected void handleCompleted() { - updateLabel(labelUpdate, getStatus(), getData()); + notifyPropertiesUpdateCompleted(propertiesUpdates[idx]); + updateLabel(labelUpdates[idx], getStatus(), getData()); } }); } - propertiesProvider.update(propertiesUpdates); + notifyPropertiesUpdatesStarted(propertiesUpdates); + propertiesProvider.update(propertiesUpdates); + } + + private void notifyPropertiesUpdatesStarted(IPropertiesUpdate[] updates) { + IPropertiesUpdateListener[] listeners = null; + synchronized(this) { + listeners = fListeners; + } + for (IPropertiesUpdateListener listener : listeners) { + listener.propertiesUpdatesStarted(updates); + } + } + + private void notifyPropertiesUpdateCompleted(IPropertiesUpdate update) { + IPropertiesUpdateListener[] listeners = null; + synchronized(this) { + listeners = fListeners; + } + for (IPropertiesUpdateListener listener : listeners) { + listener.propertiesUpdateCompleted(update); + } } /** diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/PropertiesUpdateStatus.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/PropertiesUpdateStatus.java new file mode 100644 index 00000000000..5ff39b4c9da --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/PropertiesUpdateStatus.java @@ -0,0 +1,218 @@ +/******************************************************************************* + * Copyright (c) 2010 Wind River 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.ui.viewmodel.properties; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.eclipse.cdt.dsf.concurrent.DsfMultiStatus; +import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; +import org.eclipse.core.runtime.IStatus; + +/** + * Status object for use with the IPropertiesUpdate. This status class + * allows setting a different status result for each property. This allows + * for better interpretation of status by the client of the update. + * <p> + * This status class derives from MultiStatus class such that the status + * objects for each property can be accessed through the standard + * {@link #getChildren()} method. Also, multiple properties can reference + * the same status object, meaning that the number of properties returned + * by {@link #getProperties()} may be greater than the status objects + * returned by <code>getChildren()</code>. + * <p> + * The properties status object does not have its own message, severity, + * error status or exception. All these attributes are calculated from + * the child status objects. If the status has more than one status child, + * the String returned by {@link #getMessage()} is: "Multiple errors reported". + * + * @since 2.2 + */ +public class PropertiesUpdateStatus extends DsfMultiStatus { + + final private Map<String,IStatus> fPropertiesStatus = new HashMap<String, IStatus>(1); + private boolean fFirstStatusSet; + + public PropertiesUpdateStatus() { + super(DsfUIPlugin.PLUGIN_ID, 0, "", null); //$NON-NLS-1$ + } + + /** + * Returns set of properties that have an additional status specified. + */ + public Set<String> getProperties() { + return fPropertiesStatus.keySet(); + } + + /** + * Returns an additional status for the given property in a property + * update. Returned value may be <code>null</code> if no additional + * status is given. + */ + public IStatus getStatus(String property) { + return fPropertiesStatus.get(property); + } + + /** + * Sets the given status for the given property. + */ + public void setStatus(String property, IStatus status) { + IStatus child = findEquivalentChild(status); + if (child != null) { + status = child; + } else { + add(status); + } + + fPropertiesStatus.put(property, status); + } + + /** + * Sets the given status for the properties array. + */ + public void setStatus(String[] properties, IStatus status) { + IStatus child = findEquivalentChild(status); + if (child != null) { + status = child; + } else { + add(status); + } + + for (String property : properties) { + fPropertiesStatus.put(property, status); + } + } + + /** + * Merges data in the new status into the base status data, and returns the + * resulting status. Only properties specified in the given set are merged. + * <p> + * The new status is considered to be more up to date than the base + * status and its data overrides the base status . If the base status + * holds an error for a given property, which is found in the + * given set, and the new status does not, then the base error status is + * removed. + * + * @param baseStatus Properties into which the new status properties will + * be merged. + * @param newStatus Properties status to merge. + * @param properties The properties to consider in the new status. + * @return Resulting merged status object. + */ + public static PropertiesUpdateStatus mergePropertiesStatus(PropertiesUpdateStatus baseStatus, + PropertiesUpdateStatus newStatus, Set<String> properties) + { + PropertiesUpdateStatus mergedStatus = new PropertiesUpdateStatus(); + mergedStatus.fPropertiesStatus.putAll(baseStatus.fPropertiesStatus); + + for (String property : properties) { + IStatus propertyStatus = newStatus.getStatus(property); + if (propertyStatus != null) { + mergedStatus.fPropertiesStatus.put(property, propertyStatus); + } else { + mergedStatus.fPropertiesStatus.remove(property); + } + } + Set<IStatus> children = new HashSet<IStatus>((baseStatus.getChildren().length + newStatus.getChildren().length) * 4/3); + + children.addAll(mergedStatus.fPropertiesStatus.values()); + for (IStatus child : children) { + mergedStatus.add(child); + } + + return mergedStatus; + } + + /** + * Adds the given status object as a child of this status. If there's an + * equivalent child status already, the new status is ignored. + */ + @Override + public void add(IStatus status) { + if (findEquivalentChild(status) != null) { + return; + } + + super.add(status); + + boolean firstSet; + synchronized(this) { + firstSet = fFirstStatusSet; + fFirstStatusSet = true; + } + + if (!firstSet) { + setMessage(status.getMessage()); + } else { + setMessage(MessagesForProperties.PropertiesUpdateStatus_message); + } + } + + /** + * Finds a child status that is equivalent to the given status. + */ + private IStatus findEquivalentChild(IStatus status) { + if (getChildren().length != 0) { + for (IStatus child : getChildren()) { + if (areEquivalent(child, status)) { + return child; + } + } + } + return null; + } + + /** + * Compares two status objects to determine if they are equivalent. + */ + private boolean areEquivalent(IStatus s1, IStatus s2) { + if ( (s1 == null && s2 != null) || (s1 != null && s2 == null) ) + { + return false; + } + if (s1 == null) { + return true; + } + if ( (s1.getSeverity() != s2.getSeverity()) || + !s1.getPlugin().equals(s2.getPlugin()) || + (s1.getCode() != s2.getCode()) ) + { + return false; + } + if ( (s1.getException() == null && s1.getException() != null) || + (s1.getException() != null && s1.getException() == null) || + (s1.getException() != null && !s1.getException().equals(s2.getException())) ) + { + return false; + } + return s1.getMessage().equals(s2.getMessage()); + }; + + + /** + * Convenience method that returns and optionally creates a properties + * update status object for the given update. + */ + public static PropertiesUpdateStatus getPropertiesStatus(IPropertiesUpdate update) { + IStatus updateStatus = update.getStatus(); + if (updateStatus instanceof PropertiesUpdateStatus) { + return (PropertiesUpdateStatus)updateStatus; + } else { + PropertiesUpdateStatus propertiesStatus = new PropertiesUpdateStatus(); + if (!updateStatus.isOK()) { + propertiesStatus.add(updateStatus); + } + return propertiesStatus; + } + } +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/VMDelegatingPropertiesUpdate.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/VMDelegatingPropertiesUpdate.java index 296adcfe8f4..97638aa411e 100644 --- a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/VMDelegatingPropertiesUpdate.java +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/VMDelegatingPropertiesUpdate.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2009 Wind River Systems and others. + * Copyright (c) 2009, 2010 Wind River 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 @@ -15,6 +15,7 @@ import java.util.Set; import org.eclipse.cdt.dsf.concurrent.RequestMonitor; import org.eclipse.cdt.dsf.ui.viewmodel.VMViewerUpdate; +import org.eclipse.core.runtime.IStatus; /** * Properties update used as to collect property data from the provider. @@ -45,6 +46,22 @@ public class VMDelegatingPropertiesUpdate extends VMViewerUpdate implements IPro fParentUpdate.setAllProperties(properties); } + /** + * @since 2.2 + */ + @Override + public IStatus getStatus() { + return fParentUpdate.getStatus(); + } + + /** + * @since 2.2 + */ + @Override + public void setStatus(IStatus status) { + fParentUpdate.setStatus(status); + } + @Override public String toString() { return "VMDelegatingPropertiesUpdate -> " + fParentUpdate; //$NON-NLS-1$ diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/VMPropertiesUpdate.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/VMPropertiesUpdate.java index a645c34d6fc..d02f3cbff86 100644 --- a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/VMPropertiesUpdate.java +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/properties/VMPropertiesUpdate.java @@ -22,12 +22,20 @@ import org.eclipse.cdt.dsf.internal.DsfPlugin; import org.eclipse.cdt.dsf.internal.LoggingUtils; import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin; import org.eclipse.cdt.dsf.ui.viewmodel.VMViewerUpdate; +import org.eclipse.core.runtime.IStatus; import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate; import org.eclipse.jface.viewers.TreePath; /** - * Properties update used as to collect property data from the provider. + * Properties update used as to collect property data from the provider. + * <p> + * The status returned by the VMPropertiesUpdate is always going to be of type + * PropertiesUpdateStatus, which allows for setting status for individual + * properties. + * </p> + * + * @see PropertiesUpdateStatus * * @since 2.0 */ @@ -51,11 +59,13 @@ public class VMPropertiesUpdate extends VMViewerUpdate implements IPropertiesUpd public VMPropertiesUpdate(Set<String> properties, IViewerUpdate parentUpdate, DataRequestMonitor<Map<String,Object>> rm) { super(parentUpdate, rm); + super.setStatus(new PropertiesUpdateStatus()); fProperties = properties; } public VMPropertiesUpdate(Set<String> properties, TreePath elementPath, Object viewerInput, IPresentationContext presentationContext, DataRequestMonitor<Map<String,Object>> rm) { super(elementPath, viewerInput, presentationContext, rm); + super.setStatus(new PropertiesUpdateStatus()); fProperties = properties; } @@ -64,6 +74,13 @@ public class VMPropertiesUpdate extends VMViewerUpdate implements IPropertiesUpd return fProperties; } + /** + * @since 2.2 + */ + public Map<String, Object> getValues() { + return fValues; + } + public synchronized void setProperty(String property, Object value) { if (!fCreatedOwnMap) { fCreatedOwnMap = true; @@ -86,6 +103,25 @@ public class VMPropertiesUpdate extends VMViewerUpdate implements IPropertiesUpd } /** + * Overrides the base class to implement special handling of + * {@link PropertiesUpdateStatus}. If the given status is an instance of + * properties status, this new status will be set to the update. Otherwise, the + * given status will be merged into the updates existing properties status. + * This way {@link #getStatus()} should always return an instance of + * <code>PropertiesUpdateStatus</code>. + */ + @Override + public void setStatus(IStatus status) { + if (status instanceof PropertiesUpdateStatus) { + super.setStatus(status); + } else if ((getStatus() instanceof PropertiesUpdateStatus)) { + ((PropertiesUpdateStatus)getStatus()).add(status); + } else { + assert false : "VMPropertiesUpdate status should always be a PropertiesUpdateStatus"; //$NON-NLS-1$ + } + } + + /** * Overrides the standard done in order to store the retrieved values * in the client's request monitor. */ diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/AbstractCachingVMProvider.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/AbstractCachingVMProvider.java index 0340def2021..ad244d76c7c 100644 --- a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/AbstractCachingVMProvider.java +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/AbstractCachingVMProvider.java @@ -14,10 +14,12 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Set; import org.eclipse.cdt.dsf.concurrent.CountingRequestMonitor; import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; @@ -40,6 +42,7 @@ import org.eclipse.cdt.dsf.ui.viewmodel.VMChildrenUpdate; import org.eclipse.cdt.dsf.ui.viewmodel.VMHasChildrenUpdate; 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.PropertiesUpdateStatus; import org.eclipse.cdt.dsf.ui.viewmodel.properties.VMPropertiesUpdate; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Platform; @@ -62,7 +65,7 @@ import org.eclipse.swt.widgets.TreeItem; * @since 1.0 */ public class AbstractCachingVMProvider extends AbstractVMProvider - implements ICachingVMProvider, IElementPropertiesProvider, ICachingVMProviderExtension + implements ICachingVMProvider, IElementPropertiesProvider, ICachingVMProviderExtension, ICachingVMProviderExtension2 { /** * @since 2.0 @@ -175,7 +178,7 @@ public class AbstractCachingVMProvider extends AbstractVMProvider /** * Entry with cached element data. */ - private static class ElementDataEntry extends Entry { + private static class ElementDataEntry extends Entry implements ICacheEntry { ElementDataEntry(ElementDataKey key) { super(key); } @@ -254,6 +257,16 @@ public class AbstractCachingVMProvider extends AbstractVMProvider ", properties=" + fProperties + //$NON-NLS-1$ ", oldProperties=" + fArchiveProperties + "]"; //$NON-NLS-1$ //$NON-NLS-2$ } + + public IVMNode getNode() { return ((ElementDataKey)fKey).fNode; } + public Object getViewerInput() { return ((ElementDataKey)fKey).fViewerInput; } + public TreePath getElementPath() { return ((ElementDataKey)fKey).fPath; } + public boolean isDirty() { return fDirty; } + public Boolean getHasChildren() { return fHasChildren; } + public Integer getChildCount() { return fChildrenCount; } + public Map<Integer, Object> getChildren() { return fChildren; } + public Map<String, Object> getProperties() { return fProperties; } + public java.util.Map<String,Object> getArchiveProperties() { return fArchiveProperties; } } /** @@ -294,7 +307,6 @@ public class AbstractCachingVMProvider extends AbstractVMProvider public String toString() { return fElementTester.toString() + " " + fRootElement.toString(); //$NON-NLS-1$ } - } /** @@ -443,13 +455,18 @@ public class AbstractCachingVMProvider extends AbstractVMProvider } } + public ICacheEntry getCacheEntry(IVMNode node, Object viewerInput, TreePath path) { + ElementDataKey key = makeEntryKey(node, viewerInput, path); + return getElementDataEntry(key, false); + } + @Override public void updateNode(final IVMNode node, IHasChildrenUpdate[] updates) { LinkedList <IHasChildrenUpdate> missUpdates = new LinkedList<IHasChildrenUpdate>(); for(final IHasChildrenUpdate update : updates) { // Find or create the cache entry for the element of this update. ElementDataKey key = makeEntryKey(node, update); - final ElementDataEntry entry = getElementDataEntry(key); + final ElementDataEntry entry = getElementDataEntry(key, true); updateRootElementMarker(key.fRootElement, node, update); // Check if the cache entry has this request result cached. @@ -498,7 +515,7 @@ public class AbstractCachingVMProvider extends AbstractVMProvider public void updateNode(final IVMNode node, final IChildrenCountUpdate update) { // Find or create the cache entry for the element of this update. ElementDataKey key = makeEntryKey(node, update); - final ElementDataEntry entry = getElementDataEntry(key); + final ElementDataEntry entry = getElementDataEntry(key, true); updateRootElementMarker(key.fRootElement, node, update); // Check if the cache entry has this request result cached. @@ -541,7 +558,7 @@ public class AbstractCachingVMProvider extends AbstractVMProvider public void updateNode(final IVMNode node, final IChildrenUpdate update) { // Find or create the cache entry for the element of this update. ElementDataKey key = makeEntryKey(node, update); - final ElementDataEntry entry = getElementDataEntry(key); + final ElementDataEntry entry = getElementDataEntry(key, true); updateRootElementMarker(key.fRootElement, node, update); final int flushCounter = entry.fFlushCounter; @@ -928,14 +945,21 @@ public class AbstractCachingVMProvider extends AbstractVMProvider * update and creates an element cache entry key. */ private ElementDataKey makeEntryKey(IVMNode node, IViewerUpdate update) { - Object rootElement = update.getViewerInput(); // Default + return makeEntryKey(node, update.getViewerInput(), update.getElementPath()); + } + + /** + * Convenience class that searches for the root element for the given + * update and creates an element cache entry key. + */ + private ElementDataKey makeEntryKey(IVMNode node, Object viewerInput, TreePath path) { + Object rootElement = viewerInput; // Default outer: for (IVMModelProxy proxy : getActiveModelProxies()) { Object proxyRoot = proxy.getRootElement(); - if (proxyRoot.equals(update.getViewerInput())) { + if (proxyRoot.equals(viewerInput)) { rootElement = proxyRoot; break; } - TreePath path = update.getElementPath(); for (int i = 0; i < path.getSegmentCount(); i++) { if (proxyRoot.equals(path.getSegment(i))) { rootElement = proxyRoot; @@ -944,27 +968,29 @@ public class AbstractCachingVMProvider extends AbstractVMProvider } } - return new ElementDataKey(rootElement, node, update.getViewerInput(), update.getElementPath()); + return new ElementDataKey(rootElement, node, viewerInput, path); } + /** * This is the only method that should be used to access a cache entry. * It creates a new entry if needed and it maintains the ordering in * the least-recently-used linked list. + * @param create Create the entry if needed. + * @return cache element entry, may be <code>null</code> if entry does + * not exist and the create parameter is <code>false</code> */ - private ElementDataEntry getElementDataEntry(ElementDataKey key) { + private ElementDataEntry getElementDataEntry(ElementDataKey key, boolean create) { assert key != null; ElementDataEntry entry = (ElementDataEntry)fCacheData.get(key); - if (entry == null) { + if (entry != null) { + // Entry exists, move it to the end of the list. + entry.reinsert(fCacheListHead); + } else if (create) { // Create a new entry and add it to the end of the list. entry = new ElementDataEntry(key); addEntry(key, entry); - } else { - // Entry exists, move it to the end of the list. - entry.reinsert(fCacheListHead); } - - return entry; } @@ -991,7 +1017,7 @@ public class AbstractCachingVMProvider extends AbstractVMProvider if (created) { ElementDataKey rootElementDataKey = new ElementDataKey(rootElement, node, update.getViewerInput(), update.getElementPath()); - ElementDataEntry entry = getElementDataEntry(rootElementDataKey); + ElementDataEntry entry = getElementDataEntry(rootElementDataKey, false); Object[] rootElementChildren = getActiveUpdatePolicy().getInitialRootElementChildren(rootElement); if (rootElementChildren != null) { @@ -1104,7 +1130,7 @@ public class AbstractCachingVMProvider extends AbstractVMProvider for(final IPropertiesUpdate update : updates) { // Find or create the cache entry for the element of this update. ElementDataKey key = makeEntryKey(node, update); - final ElementDataEntry entry = getElementDataEntry(key); + final ElementDataEntry entry = getElementDataEntry(key, true); updateRootElementMarker(key.fRootElement, node, update); // The request can be retrieved from cache if all the properties that were requested in the update are @@ -1120,28 +1146,59 @@ public class AbstractCachingVMProvider extends AbstractVMProvider update.setAllProperties(entry.fProperties); update.setStatus((IStatus)entry.fProperties.get(PROP_UPDATE_STATUS)); update.done(); - } else if (entry.fProperties != null && entry.fDirty) { - // Cache miss, BUT the entry is dirty already. Rather then fetch new data from model, return - // incomplete data to user. User can refresh the view to get the complete data set. - if (DEBUG_CACHE && (DEBUG_PRESENTATION_ID == null || getPresentationContext().getId().equals(DEBUG_PRESENTATION_ID))) { - DsfUIPlugin.debug("cacheHitPropertiesPartialStaleData(node = " + node + ", update = " + update + ", " + entry.fProperties + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ - } - if (entry.fProperties.containsKey(PROP_UPDATE_POLICY_ID)) { - entry.fProperties.put(PROP_UPDATE_POLICY_ID, getActiveUpdatePolicy().getID()); - } - update.setAllProperties(entry.fProperties); - update.setStatus(DsfUIPlugin.newErrorStatus(IDsfStatusConstants.INVALID_STATE, "Cache contains partial stale data for this request.", null)); //$NON-NLS-1$ - update.done(); } else { - // Cache miss! Save the flush counter of the entry and create a proxy update. + // Cache miss! Check if already cached properties can be re-used. + Set<String> missingProperties = null; + if (entry.fProperties != null) { + missingProperties = new HashSet<String>(update.getProperties().size() * 4/3); + missingProperties.addAll(update.getProperties()); + missingProperties.removeAll(entry.fProperties.keySet()); + + if (entry.fDirty) { + // Cache miss, BUT the entry is dirty already. Determine which properties can still be updated + // (if any), then request the missing properties from node, or return an error. + if (getActiveUpdatePolicy() instanceof IVMUpdatePolicyExtension) { + IVMUpdatePolicyExtension updatePolicyExt = (IVMUpdatePolicyExtension)getActiveUpdatePolicy(); + for (Iterator<String> itr = missingProperties.iterator(); itr.hasNext();) { + String missingProperty = itr.next(); + if ( !updatePolicyExt.canUpdateDirtyProperty(entry, missingProperty) ) { + itr.remove(); + PropertiesUpdateStatus.getPropertiesStatus(update).setStatus( + missingProperty, + DsfUIPlugin.newErrorStatus(IDsfStatusConstants.INVALID_STATE, "Cache contains stale data. Refresh view.", null ));//$NON-NLS-1$ + } + } + } else { + PropertiesUpdateStatus.getPropertiesStatus(update).setStatus( + missingProperties.toArray(new String[missingProperties.size()]), + DsfUIPlugin.newErrorStatus(IDsfStatusConstants.INVALID_STATE, "Cache contains stale data. Refresh view.", null ));//$NON-NLS-1$ + missingProperties.clear(); + } + if (missingProperties.isEmpty()) { + if (entry.fProperties.containsKey(PROP_UPDATE_POLICY_ID)) { + entry.fProperties.put(PROP_UPDATE_POLICY_ID, getActiveUpdatePolicy().getID()); + } + update.setAllProperties(entry.fProperties); + update.done(); + return; + } + } + } else { + missingProperties = update.getProperties(); + } + + final Set<String> _missingProperties = missingProperties; + // Save the flush counter of the entry and create a proxy update. final int flushCounter = entry.fFlushCounter; missUpdates.add(new VMPropertiesUpdate( - update.getProperties(), + missingProperties, update, new ViewerDataRequestMonitor<Map<String, Object>>(getExecutor(), update) { @Override protected void handleCompleted() { - Map<String, Object> properties; + PropertiesUpdateStatus missUpdateStatus = (PropertiesUpdateStatus)getStatus(); + Map<String, Object> cachedProperties; + PropertiesUpdateStatus cachedStatus; if (!isCanceled() && flushCounter == entry.fFlushCounter) { // We are caching the result of this update. Copy the properties from the update // to the cached properties map. @@ -1150,56 +1207,67 @@ public class AbstractCachingVMProvider extends AbstractVMProvider if (update.getProperties().contains(PROP_CACHE_ENTRY_DIRTY)) { entry.fProperties.put(PROP_CACHE_ENTRY_DIRTY, entry.fDirty); } + entry.fProperties.put(PROP_UPDATE_STATUS, new PropertiesUpdateStatus()); } - properties = entry.fProperties; - properties.putAll(getData()); + cachedProperties = entry.fProperties; + cachedProperties.putAll(getData()); - // Make sure that all the properties that were - // requested by the update object are in the - // cache entry's properties map. It's possible the - // ViewerDataRequestMonitor was able to provide - // us only a subset of the requested ones. We - // want to prevent that from causing future - // cache misses, since a cache hit requires the - // cache entry to contain all requested - // properties. Use a null value for the missing - // items. - for (String updateProperty : update.getProperties()) { - if (!properties.containsKey(updateProperty)) { - properties.put(updateProperty, null); + // Make sure that all the properties that were requested by the update object are in + // the cache entry's properties map. It's possible he ViewerDataRequestMonitor was able + // to provide us only a subset of the requested ones. We want to prevent that from + // causing future cache misses, since a cache hit requires the cache entry to contain + // all requested properties. Use a null value for the missing items. + for (String property : _missingProperties) { + if (!getData().containsKey(property)) { + cachedProperties.put(property, null); } } + + // Merge status from properties that came back from the node into the status that's in + // the cache. + cachedStatus = (PropertiesUpdateStatus)cachedProperties.get(PROP_UPDATE_STATUS); + cachedStatus = PropertiesUpdateStatus.mergePropertiesStatus( + cachedStatus, missUpdateStatus, _missingProperties); + cachedProperties.put(PROP_UPDATE_STATUS, cachedStatus); } else { - // We are not caching the result of this update, but we should still - // return valid data to the client. In case the update was canceled - // we can also return valid data to the client even if the client - // is likely to ignore it since the cost of doing so is relatively low. - properties = new HashMap<String, Object>((getData().size() + 3) * 4/3); + // We are not caching the result of this update, but we should still return valid data + // to the client. In case the update was canceled we can also return valid data to the + // client even if the client is likely to ignore it since the cost of doing so is + // relatively low. + // Create a temporary cached properties map and add existing cache and node update + // properties to it. + if (entry.fProperties != null) { + cachedProperties = new HashMap<String, Object>((entry.fProperties.size() + getData().size() + 3) * 4/3); + cachedProperties.putAll(entry.fProperties); + cachedStatus = PropertiesUpdateStatus.mergePropertiesStatus( + (PropertiesUpdateStatus)cachedProperties.get(PROP_UPDATE_STATUS), + missUpdateStatus, _missingProperties); + } else { + cachedProperties = new HashMap<String, Object>((getData().size() + 3) * 4/3); + cachedStatus = missUpdateStatus; + } + cachedProperties.putAll(getData()); + cachedProperties.put(PROP_UPDATE_STATUS, missUpdateStatus); if (update.getProperties().contains(PROP_CACHE_ENTRY_DIRTY)) { - properties.put(PROP_CACHE_ENTRY_DIRTY, Boolean.TRUE); + cachedProperties.put(PROP_CACHE_ENTRY_DIRTY, Boolean.TRUE); } - properties.putAll(getData()); } + // Refresh the update policy property. if (update.getProperties().contains(PROP_UPDATE_POLICY_ID)) { - properties.put(PROP_UPDATE_POLICY_ID, getActiveUpdatePolicy().getID()); + cachedProperties.put(PROP_UPDATE_POLICY_ID, getActiveUpdatePolicy().getID()); } - // Save the update status result in the properties as well, it will be - // written to the client update when client updates are completed from - // cache. - properties.put(PROP_UPDATE_STATUS, getStatus()); - // If there is archive data available, calculate the requested changed value properties. // Do not calculate the changed flags if the entry has been flushed. if (entry.fArchiveProperties != null && flushCounter == entry.fFlushCounter) { for (String updateProperty : update.getProperties()) { if (updateProperty.startsWith(PROP_IS_CHANGED_PREFIX)) { String changedPropertyName = updateProperty.substring(LENGTH_PROP_IS_CHANGED_PREFIX); - Object newValue = properties.get(changedPropertyName); + Object newValue = cachedProperties.get(changedPropertyName); Object oldValue = entry.fArchiveProperties.get(changedPropertyName); if (oldValue != null) { - properties.put(updateProperty, !oldValue.equals(newValue)); + cachedProperties.put(updateProperty, !oldValue.equals(newValue)); } } } @@ -1209,8 +1277,14 @@ public class AbstractCachingVMProvider extends AbstractVMProvider DsfUIPlugin.debug("cacheSavedProperties(node = " + node + ", update = " + update + ", " + getData() + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ } - update.setAllProperties(properties); - update.setStatus(getStatus()); + // Fill in requested properties and status into the update. + for (String property : update.getProperties()) { + update.setProperty(property, cachedProperties.get(property)); + } + PropertiesUpdateStatus updateStatus = PropertiesUpdateStatus.getPropertiesStatus(update); + updateStatus = PropertiesUpdateStatus.mergePropertiesStatus( + updateStatus, cachedStatus, update.getProperties()); + update.setStatus(updateStatus); update.done(); } })); diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/ICacheEntry.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/ICacheEntry.java new file mode 100644 index 00000000000..6e6d71e4e8e --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/ICacheEntry.java @@ -0,0 +1,90 @@ +/******************************************************************************* + * Copyright (c) 2010 Wind River 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.ui.viewmodel.update; + +import java.util.Map; + +import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMNode; +import org.eclipse.jface.viewers.TreePath; + +/** + * Cache entry in a caching VM provider. + * + * @see ICachingVMProvider + * @see ICachingVMProviderExtension2 + * + * @since 2.2 + */ +@ConfinedToDsfExecutor("") +public interface ICacheEntry { + + /** + * The VM node that this cache entry is for. This parameter is part of the + * key to finding the cache entry. + */ + public IVMNode getNode(); + + /** + * The viewer input object that this cache entry is for. This parameter + * is part of the key to finding the cache entry. + */ + public Object getViewerInput(); + + /** + * The element path that this cache entry is for. This parameter is part + * of the key to finding the cache entry. + */ + public TreePath getElementPath(); + + /** + * Says whether this cache entry is currently marked as dirty. If a cache + * entry is dirty, it means that it contains stale data which has not been + * flushed as indicated by the cache's update policy. + * @return + */ + public boolean isDirty(); + + /** + * Returns the a flag indicating whether the element pointing to this entry + * has children. Returns <code>null</code> if this value is not known by + * cache. + */ + public Boolean getHasChildren(); + + /** + * Returns the count of children for the element belonging to this entry. + * Returns <code>null</code> if this value is not known by cache. + */ + public Integer getChildCount(); + + /** + * Returns a map of children of the element belonging to this entry. + * The returned map contains integer keys which are indexes of the + * element's children. The values in the map are the child element. + * Returns <code>null</code> if this value is not known by cache. + */ + public Map<Integer, Object> getChildren(); + + /** + * Returns map of properties of the element belonging to this entry. + * Returns <code>null</code> if this value is not known by cache. + */ + public Map<String, Object> getProperties(); + + /** + * Returns the archived map of properties of the element belong to this + * entry. The archived properties are properties which were saved when + * the cache was last flushed, as indicated by the cache's active update + * policy. Returns <code>null</code> if this value is not known by cache. + */ + public Map<String, Object> getArchiveProperties(); +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/ICachingVMProviderExtension2.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/ICachingVMProviderExtension2.java new file mode 100644 index 00000000000..594ad6a7fe9 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/ICachingVMProviderExtension2.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2010 Wind River 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.ui.viewmodel.update; + +import org.eclipse.cdt.dsf.ui.viewmodel.IVMNode; +import org.eclipse.jface.viewers.TreePath; + + +/** + * Extension allowing access to the caching VM provider cache entries. + * + * @since 2.2 + */ +public interface ICachingVMProviderExtension2 extends ICachingVMProvider { + + /** + * Returns the cache entry for the given parameters. May return <code>null</code> + * if the cache entry does not exist in the cache. + */ + public ICacheEntry getCacheEntry(IVMNode node, Object viewerInput, TreePath path); + +} diff --git a/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/IVMUpdatePolicyExtension.java b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/IVMUpdatePolicyExtension.java new file mode 100644 index 00000000000..30b386b5a84 --- /dev/null +++ b/dsf/org.eclipse.cdt.dsf.ui/src/org/eclipse/cdt/dsf/ui/viewmodel/update/IVMUpdatePolicyExtension.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2010 Wind River 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.dsf.ui.viewmodel.update; + + +/** + * Extension to the VM Update policy which allows the policy to control how to + * update missing property values in a dirty cache entry. + * + * @since 2.2 + */ +public interface IVMUpdatePolicyExtension extends IVMUpdatePolicy { + + /** + * Determines whether the given dirty cache entry should have the given + * missing property updated. + * + * @param entry The dirty cache entry that is missing the given requested + * property. + * @param property Property missing from cache. + * @return If <code>true</code> cache can update the given missing property + * in the dirty cache entry with data from the VM node. + */ + public boolean canUpdateDirtyProperty(ICacheEntry entry, String property); +} diff --git a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/IViewerUpdatesListenerConstants.java b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/IViewerUpdatesListenerConstants.java new file mode 100644 index 00000000000..eca8593d9b4 --- /dev/null +++ b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/IViewerUpdatesListenerConstants.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2009 Wind River 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.tests.dsf; + +/** + * Convenience interface with constants used by the test model update listener. + * @since 2.2 + */ +public interface IViewerUpdatesListenerConstants { + + public static final int LABEL_SEQUENCE_COMPLETE = 0X00000001; + public static final int CONTENT_SEQUENCE_COMPLETE = 0X00000002; + public static final int CONTENT_SEQUENCE_STARTED = 0X00020000; + public static final int LABEL_UPDATES = 0X00000004; + public static final int LABEL_SEQUENCE_STARTED = 0X00040000; + public static final int HAS_CHILDREN_UPDATES = 0X00000008; + public static final int HAS_CHILDREN_UPDATES_STARTED = 0X00080000; + public static final int CHILD_COUNT_UPDATES = 0X00000010; + public static final int CHILD_COUNT_UPDATES_STARTED = 0X00100000; + public static final int CHILDREN_UPDATES = 0X00000020; + public static final int CHILDREN_UPDATES_STARTED = 0X00200000; + public static final int MODEL_CHANGED_COMPLETE = 0X00000040; + public static final int MODEL_PROXIES_INSTALLED = 0X00000080; + public static final int STATE_SAVE_COMPLETE = 0X00000100; + public static final int STATE_SAVE_STARTED = 0X01000000; + public static final int STATE_RESTORE_COMPLETE = 0X00000200; + public static final int STATE_RESTORE_STARTED = 0X02000000; + public static final int STATE_UPDATES = 0X00000400; + public static final int STATE_UPDATES_STARTED = 0X04000000; + public static final int PROPERTY_UPDATES = 0X00000800; + public static final int PROPERTY_UPDATES_STARTED = 0X08000000; + + public static final int VIEWER_UPDATES_RUNNING = 0X00001000; + public static final int LABEL_UPDATES_RUNNING = 0X00002000; + + public static final int VIEWER_UPDATES_STARTED = HAS_CHILDREN_UPDATES_STARTED | CHILD_COUNT_UPDATES_STARTED | CHILDREN_UPDATES_STARTED; + + public static final int LABEL_COMPLETE = LABEL_SEQUENCE_COMPLETE | LABEL_UPDATES | LABEL_UPDATES_RUNNING; + public static final int CONTENT_UPDATES = HAS_CHILDREN_UPDATES | CHILD_COUNT_UPDATES | CHILDREN_UPDATES; + public static final int CONTENT_COMPLETE = CONTENT_UPDATES | CONTENT_SEQUENCE_COMPLETE | VIEWER_UPDATES_RUNNING; + + public static final int ALL_UPDATES_COMPLETE = LABEL_COMPLETE | CONTENT_COMPLETE | MODEL_PROXIES_INSTALLED | LABEL_UPDATES_RUNNING | VIEWER_UPDATES_RUNNING; +} diff --git a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/ServiceEventWaitor.java b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/ServiceEventWaitor.java index 6b28f74b10d..9e25b8924bd 100644 --- a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/ServiceEventWaitor.java +++ b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/ServiceEventWaitor.java @@ -12,6 +12,7 @@ package org.eclipse.cdt.tests.dsf; import org.eclipse.cdt.dsf.service.DsfServiceEventHandler; import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.swt.widgets.Display; /* * This class provides a way to wait for an asynchronous ServerEvent @@ -34,14 +35,16 @@ public class ServiceEventWaitor<V> { /* The type of event to wait for */ private Class<V> fEventTypeClass; - private DsfSession fSession; + private final DsfSession fSession; private V fEvent; + private final Display fDisplay; /* Empty contructor. registerForEvent() should be called when * this constructor is used. */ public ServiceEventWaitor(DsfSession session) { + fDisplay = Display.getDefault(); fSession = session; } @@ -62,10 +65,12 @@ public class ServiceEventWaitor<V> { fSession.addServiceEventListener(this, null); } - @Override - protected void finalize() throws Throwable { - super.finalize(); - if (fEventTypeClass != null) fSession.removeServiceEventListener(this); + public DsfSession getSession() { + return fSession; + } + + public void dispose() { + if (fEventTypeClass != null) fSession.removeServiceEventListener(this); } /* Block until 'timeout' or the previously specified event has been @@ -80,7 +85,15 @@ public class ServiceEventWaitor<V> { // The event might have already been received if (fEvent != null) return fEvent; - wait(timeout); + long timeoutTime = System.currentTimeMillis() + timeout; + while (timeoutTime > System.currentTimeMillis()) { + if (fEvent != null) { + break; + } + if (!fDisplay.readAndDispatch()) { + Thread.sleep(0); + } + } if (fEvent == null) { throw new Exception("Timed out waiting for ServiceEvent: " + fEventTypeClass.getName()); diff --git a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/ViewerUpdatesListener.java b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/ViewerUpdatesListener.java new file mode 100644 index 00000000000..33985c1d252 --- /dev/null +++ b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/ViewerUpdatesListener.java @@ -0,0 +1,644 @@ +/******************************************************************************* + * Copyright (c) 2009 Wind River 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.tests.dsf; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +import junit.framework.Assert; + +import org.eclipse.cdt.dsf.ui.viewmodel.properties.IPropertiesUpdate; +import org.eclipse.cdt.dsf.ui.viewmodel.properties.IPropertiesUpdateListener; +import org.eclipse.debug.internal.ui.viewers.model.ILabelUpdateListener; +import org.eclipse.debug.internal.ui.viewers.model.ITreeModelViewer; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; +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.IModelChangedListener; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxy; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IStateUpdateListener; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdateListener; +import org.eclipse.jface.viewers.TreePath; + +/** + * @since 2.2 + */ +public class ViewerUpdatesListener + implements IViewerUpdateListener, ILabelUpdateListener, IModelChangedListener, IViewerUpdatesListenerConstants, + IStateUpdateListener, IPropertiesUpdateListener +{ + private ITreeModelViewer fViewer; + + private boolean fFailOnRedundantUpdates; + private Set<IViewerUpdate> fRedundantUpdates = new HashSet<IViewerUpdate>(); + + private boolean fFailOnMultipleModelUpdateSequences; + private boolean fMultipleModelUpdateSequencesObserved; + private boolean fFailOnMultipleLabelUpdateSequences; + private boolean fMultipleLabelUpdateSequencesObserved; + + private Set<TreePath> fHasChildrenUpdatesScheduled = makeTreePathSet(); + private Set<IViewerUpdate> fHasChildrenUpdatesRunning = new HashSet<IViewerUpdate>(); + private Set<IViewerUpdate> fHasChildrenUpdatesCompleted = new HashSet<IViewerUpdate>(); + private Map<TreePath, Set<Integer>> fChildrenUpdatesScheduled = makeTreePathMap(); + private Set<IViewerUpdate> fChildrenUpdatesRunning = new HashSet<IViewerUpdate>(); + private Set<IViewerUpdate> fChildrenUpdatesCompleted = new HashSet<IViewerUpdate>(); + private Set<TreePath> fChildCountUpdatesScheduled = makeTreePathSet(); + private Set<IViewerUpdate> fChildCountUpdatesRunning = new HashSet<IViewerUpdate>(); + private Set<IViewerUpdate> fChildCountUpdatesCompleted = new HashSet<IViewerUpdate>(); + private Set<TreePath> fLabelUpdates = makeTreePathSet(); + private Set<IViewerUpdate> fLabelUpdatesRunning = new HashSet<IViewerUpdate>(); + private Set<IViewerUpdate> fLabelUpdatesCompleted = new HashSet<IViewerUpdate>(); + private Set<TreePath> fPropertiesUpdates = makeTreePathSet(); + private Set<IViewerUpdate> fPropertiesUpdatesRunning = new HashSet<IViewerUpdate>(); + private Set<IViewerUpdate> fPropertiesUpdatesCompleted = new HashSet<IViewerUpdate>(); +// private Set<TreePath> fProxyModels = new HashSet<TreePath>(); + private Set<TreePath> fStateUpdates = makeTreePathSet(); + private boolean fContentSequenceStarted; + private boolean fContentSequenceComplete; + private boolean fLabelUpdatesStarted; + private boolean fLabelSequenceComplete; + private boolean fModelChangedComplete; + private boolean fStateSaveStarted; + private boolean fStateSaveComplete; + private boolean fStateRestoreStarted; + private boolean fStateRestoreComplete; + private int fContentUpdatesCounter; + private int fLabelUpdatesCounter; + private int fPropertiesUpdatesCounter; + private int fTimeoutInterval = 60000; + private long fTimeoutTime; + + protected Set<TreePath> makeTreePathSet() { + return new HashSet<TreePath>(); + } + + protected <V> Map<TreePath, V> makeTreePathMap() { + return new HashMap<TreePath, V>(); + } + + + public ViewerUpdatesListener(ITreeModelViewer viewer, boolean failOnRedundantUpdates, boolean failOnMultipleModelUpdateSequences) { + this(viewer); + setFailOnRedundantUpdates(failOnRedundantUpdates); + setFailOnMultipleModelUpdateSequences(failOnMultipleModelUpdateSequences); + } + + public ViewerUpdatesListener() { + // No viewer to register with. Client will have to register the listener manually. + } + + public ViewerUpdatesListener(ITreeModelViewer viewer) { + fViewer = viewer; + fViewer.addLabelUpdateListener(this); + fViewer.addModelChangedListener(this); + fViewer.addStateUpdateListener(this); + fViewer.addViewerUpdateListener(this); + } + + public void dispose() { + if (fViewer != null) { + fViewer.removeLabelUpdateListener(this); + fViewer.removeModelChangedListener(this); + fViewer.removeStateUpdateListener(this); + fViewer.removeViewerUpdateListener(this); + fViewer = null; + } + } + + + public void setFailOnRedundantUpdates(boolean failOnRedundantUpdates) { + fFailOnRedundantUpdates = failOnRedundantUpdates; + } + + public void setFailOnMultipleModelUpdateSequences(boolean failOnMultipleLabelUpdateSequences) { + fFailOnMultipleModelUpdateSequences = failOnMultipleLabelUpdateSequences; + } + + public void setFailOnMultipleLabelUpdateSequences(boolean failOnMultipleLabelUpdateSequences) { + fFailOnMultipleLabelUpdateSequences = failOnMultipleLabelUpdateSequences; + } + + /** + * Sets the the maximum amount of time (in milliseconds) that the update listener + * is going to wait. If set to -1, the listener will wait indefinitely. + */ + public void setTimeoutInterval(int milis) { + fTimeoutInterval = milis; + } + + public void reset() { + fRedundantUpdates.clear(); + fMultipleLabelUpdateSequencesObserved = false; + fMultipleModelUpdateSequencesObserved = false; + fHasChildrenUpdatesScheduled.clear(); + fHasChildrenUpdatesRunning.clear(); + fHasChildrenUpdatesCompleted.clear(); + fChildrenUpdatesScheduled.clear(); + fChildrenUpdatesRunning.clear(); + fChildrenUpdatesCompleted.clear(); + fChildCountUpdatesScheduled.clear(); + fChildCountUpdatesRunning.clear(); + fChildCountUpdatesCompleted.clear(); + fLabelUpdates.clear(); + fLabelUpdatesRunning.clear(); + fLabelUpdatesCompleted.clear(); +// fProxyModels.clear(); + fContentSequenceStarted = false; + fContentSequenceComplete = false; + fLabelUpdatesStarted = false; + fLabelSequenceComplete = false; + fStateSaveStarted = false; + fStateSaveComplete = false; + fStateRestoreStarted = false; + fStateRestoreComplete = false; + fTimeoutTime = System.currentTimeMillis() + fTimeoutInterval; + resetModelChanged(); + } + + public void resetModelChanged() { + fModelChangedComplete = false; + } + + public void addHasChildrenUpdate(TreePath path) { + fHasChildrenUpdatesScheduled.add(path); + } + + public void removeHasChildrenUpdate(TreePath path) { + fHasChildrenUpdatesScheduled.remove(path); + } + + public void addChildCountUpdate(TreePath path) { + fChildCountUpdatesScheduled.add(path); + } + + public void removeChildreCountUpdate(TreePath path) { + fChildCountUpdatesScheduled.remove(path); + } + + public void addChildreUpdate(TreePath path, int index) { + Set<Integer> childrenIndexes = fChildrenUpdatesScheduled.get(path); + if (childrenIndexes == null) { + childrenIndexes = new TreeSet<Integer>(); + fChildrenUpdatesScheduled.put(path, childrenIndexes); + } + childrenIndexes.add(new Integer(index)); + } + + public void removeChildrenUpdate(TreePath path, int index) { + Set<Integer> childrenIndexes = fChildrenUpdatesScheduled.get(path); + if (childrenIndexes != null) { + childrenIndexes.remove(new Integer(index)); + if (childrenIndexes.isEmpty()) { + fChildrenUpdatesScheduled.remove(path); + } + } + } + + public void addLabelUpdate(TreePath path) { + fLabelUpdates.add(path); + } + + public void addPropertiesUpdate(TreePath path) { + fPropertiesUpdates.add(path); + } + + public void removeLabelUpdate(TreePath path) { + fLabelUpdates.remove(path); + } + + public void addStateUpdate(TreePath path) { + fStateUpdates.add(path); + } + + public void removeStateUpdate(TreePath path) { + fStateUpdates.remove(path); + } + + + public boolean isFinished() { + return isFinished(ALL_UPDATES_COMPLETE); + } + + public boolean isTimedOut() { + return fTimeoutInterval > 0 && fTimeoutTime < System.currentTimeMillis(); + } + + public boolean isFinished(int flags) { + if (isTimedOut()) { + throw new RuntimeException("Timed Out: " + toString(flags)); + } + + if (fFailOnRedundantUpdates && !fRedundantUpdates.isEmpty()) { + Assert.fail("Redundant Updates: " + fRedundantUpdates.toString()); + } + if (fFailOnMultipleLabelUpdateSequences && !fMultipleLabelUpdateSequencesObserved) { + Assert.fail("Multiple label update sequences detected"); + } + if (fFailOnMultipleModelUpdateSequences && fMultipleModelUpdateSequencesObserved) { + Assert.fail("Multiple viewer update sequences detected"); + } + + if ( (flags & LABEL_SEQUENCE_COMPLETE) != 0) { + if (!fLabelSequenceComplete) return false; + } + if ( (flags & LABEL_SEQUENCE_STARTED) != 0) { + if (!fLabelUpdatesStarted) return false; + } + if ( (flags & LABEL_UPDATES) != 0) { + if (!fLabelUpdates.isEmpty()) return false; + } + if ( (flags & CONTENT_SEQUENCE_STARTED) != 0) { + if (!fContentSequenceStarted) return false; + } + if ( (flags & CONTENT_SEQUENCE_COMPLETE) != 0) { + if (!fContentSequenceComplete) return false; + } + if ( (flags & HAS_CHILDREN_UPDATES_STARTED) != 0) { + if (fHasChildrenUpdatesRunning.isEmpty() && fHasChildrenUpdatesCompleted.isEmpty()) return false; + } + if ( (flags & HAS_CHILDREN_UPDATES) != 0) { + if (!fHasChildrenUpdatesScheduled.isEmpty()) return false; + } + if ( (flags & CHILD_COUNT_UPDATES_STARTED) != 0) { + if (fChildCountUpdatesRunning.isEmpty() && fChildCountUpdatesCompleted.isEmpty()) return false; + } + if ( (flags & CHILD_COUNT_UPDATES) != 0) { + if (!fChildCountUpdatesScheduled.isEmpty()) return false; + } + if ( (flags & CHILDREN_UPDATES_STARTED) != 0) { + if (fChildrenUpdatesRunning.isEmpty() && fChildrenUpdatesCompleted.isEmpty()) return false; + } + if ( (flags & CHILDREN_UPDATES) != 0) { + if (!fChildrenUpdatesScheduled.isEmpty()) return false; + } + if ( (flags & MODEL_CHANGED_COMPLETE) != 0) { + if (!fModelChangedComplete) return false; + } + if ( (flags & STATE_SAVE_COMPLETE) != 0) { + if (!fStateSaveComplete) return false; + } + if ( (flags & STATE_SAVE_STARTED) != 0) { + if (!fStateSaveStarted) return false; + } + if ( (flags & STATE_RESTORE_COMPLETE) != 0) { + if (!fStateRestoreComplete) return false; + } + if ( (flags & STATE_RESTORE_STARTED) != 0) { + if (!fStateRestoreStarted) return false; + } +// if ( (flags & MODEL_PROXIES_INSTALLED) != 0) { +// if (fProxyModels.size() != 0) return false; +// } + if ( (flags & VIEWER_UPDATES_RUNNING) != 0) { + if (fContentUpdatesCounter != 0) { + return false; + } + } + if ( (flags & LABEL_UPDATES_RUNNING) != 0) { + if (fLabelUpdatesCounter != 0) { + return false; + } + } + if ( (flags & PROPERTY_UPDATES) != 0) { + if (!fPropertiesUpdates.isEmpty()) return false; + } + if ( (flags & PROPERTY_UPDATES_STARTED) != 0) { + if (fPropertiesUpdatesRunning.isEmpty() && fPropertiesUpdatesCompleted.isEmpty()) return false; + } + + return true; + } + + public void updateStarted(IViewerUpdate update) { + synchronized (this) { + fContentUpdatesCounter++; + if (update instanceof IHasChildrenUpdate) { + fHasChildrenUpdatesRunning.add(update); + } if (update instanceof IChildrenCountUpdate) { + fChildCountUpdatesRunning.add(update); + } else if (update instanceof IChildrenUpdate) { + fChildCountUpdatesRunning.add(update); + } + } + } + + public void updateComplete(IViewerUpdate update) { + synchronized (this) { + fContentUpdatesCounter--; + } + + if (!update.isCanceled()) { + if (update instanceof IHasChildrenUpdate) { + fHasChildrenUpdatesRunning.remove(update); + fHasChildrenUpdatesCompleted.add(update); + if (!fHasChildrenUpdatesScheduled.remove(update.getElementPath()) && fFailOnRedundantUpdates) { + fRedundantUpdates.add(update); + } + } if (update instanceof IChildrenCountUpdate) { + fChildCountUpdatesRunning.remove(update); + fChildCountUpdatesCompleted.add(update); + if (!fChildCountUpdatesScheduled.remove(update.getElementPath()) && fFailOnRedundantUpdates) { + fRedundantUpdates.add(update); + } + } else if (update instanceof IChildrenUpdate) { + fChildrenUpdatesRunning.remove(update); + fChildrenUpdatesCompleted.add(update); + + int start = ((IChildrenUpdate)update).getOffset(); + int end = start + ((IChildrenUpdate)update).getLength(); + + Set<Integer> childrenIndexes = fChildrenUpdatesScheduled.get(update.getElementPath()); + if (childrenIndexes != null) { + for (int i = start; i < end; i++) { + childrenIndexes.remove(new Integer(i)); + } + if (childrenIndexes.isEmpty()) { + fChildrenUpdatesScheduled.remove(update.getElementPath()); + } + } else if (fFailOnRedundantUpdates) { + fRedundantUpdates.add(update); + } + } + } + } + + public void viewerUpdatesBegin() { + if (fFailOnMultipleModelUpdateSequences && fContentSequenceComplete) { + fMultipleModelUpdateSequencesObserved = true; + } + fContentSequenceStarted = true; + } + + public void viewerUpdatesComplete() { + fContentSequenceComplete = true; + } + + public void labelUpdateComplete(ILabelUpdate update) { + synchronized (this) { + fLabelUpdatesRunning.remove(update); + fLabelUpdatesCompleted.add(update); + fLabelUpdatesCounter--; + } + if (!fLabelUpdates.remove(update.getElementPath()) && fFailOnRedundantUpdates) { + fRedundantUpdates.add(update); + } + } + + public void labelUpdateStarted(ILabelUpdate update) { + synchronized (this) { + fLabelUpdatesRunning.add(update); + fLabelUpdatesCounter++; + } + } + + public void labelUpdatesBegin() { + if (fFailOnMultipleLabelUpdateSequences && fLabelSequenceComplete) { + fMultipleLabelUpdateSequencesObserved = true; + } + fLabelUpdatesStarted = true; + } + + public void labelUpdatesComplete() { + fLabelSequenceComplete = true; + } + + public void propertiesUpdatesStarted(IPropertiesUpdate[] updates) { + for (IPropertiesUpdate update : updates) { + fPropertiesUpdatesRunning.add(update); + fPropertiesUpdatesCounter++; + } + } + + public void propertiesUpdateCompleted(IPropertiesUpdate update) { + synchronized (this) { + fPropertiesUpdatesRunning.remove(update); + fPropertiesUpdatesCompleted.add(update); + fPropertiesUpdatesCounter--; + } + if (!fPropertiesUpdates.remove(update.getElementPath()) && fFailOnRedundantUpdates) { + fRedundantUpdates.add(update); + } + + } + + public void modelChanged(IModelDelta delta, IModelProxy proxy) { + fModelChangedComplete = true; + } + + public void stateRestoreUpdatesBegin(Object input) { + fStateRestoreStarted = true; + } + + public void stateRestoreUpdatesComplete(Object input) { + fStateRestoreComplete = true; + } + + public void stateSaveUpdatesBegin(Object input) { + fStateSaveStarted = true; + } + + public void stateSaveUpdatesComplete(Object input) { + fStateSaveComplete = true; + } + + public void stateUpdateComplete(Object input, IViewerUpdate update) { + } + + public void stateUpdateStarted(Object input, IViewerUpdate update) { + } + + private String toString(int flags) { + StringBuffer buf = new StringBuffer("Viewer Update Listener"); + + if (fFailOnRedundantUpdates) { + buf.append("\n\t"); + buf.append("fRedundantUpdates = "); + buf.append( toStringViewerUpdatesSet(fRedundantUpdates) ); + } + if (fFailOnMultipleLabelUpdateSequences) { + buf.append("\n\t"); + buf.append("fMultipleLabelUpdateSequencesObserved = " + fMultipleLabelUpdateSequencesObserved); + } + if (fFailOnMultipleModelUpdateSequences) { + buf.append("\n\t"); + buf.append("fMultipleModelUpdateSequencesObserved = " + fMultipleModelUpdateSequencesObserved); + } + if ( (flags & LABEL_SEQUENCE_COMPLETE) != 0) { + buf.append("\n\t"); + buf.append("fLabelSequenceComplete = " + fLabelSequenceComplete); + } + if ( (flags & LABEL_UPDATES_RUNNING) != 0) { + buf.append("\n\t"); + buf.append("fLabelUpdatesRunning = " + fLabelUpdatesCounter); + } + if ( (flags & LABEL_SEQUENCE_STARTED) != 0) { + buf.append("\n\t"); + buf.append("fLabelUpdatesRunning = "); + buf.append( toStringViewerUpdatesSet(fLabelUpdatesRunning) ); + buf.append("\n\t"); + buf.append("fLabelUpdatesCompleted = "); + buf.append( toStringViewerUpdatesSet(fLabelUpdatesCompleted) ); + } + if ( (flags & LABEL_UPDATES) != 0) { + buf.append("\n\t"); + buf.append("fLabelUpdates = "); + buf.append( toString(fLabelUpdates) ); + } + if ( (flags & CONTENT_SEQUENCE_COMPLETE) != 0) { + buf.append("\n\t"); + buf.append("fContentSequenceComplete = " + fContentSequenceComplete); + } + if ( (flags & VIEWER_UPDATES_RUNNING) != 0) { + buf.append("\n\t"); + buf.append("fContentUpdatesCounter = " + fContentUpdatesCounter); + } + if ( (flags & HAS_CHILDREN_UPDATES_STARTED) != 0) { + buf.append("\n\t"); + buf.append("fHasChildrenUpdatesRunning = "); + buf.append( toStringViewerUpdatesSet(fHasChildrenUpdatesRunning) ); + buf.append("\n\t"); + buf.append("fHasChildrenUpdatesCompleted = "); + buf.append( toStringViewerUpdatesSet(fHasChildrenUpdatesCompleted) ); + } + if ( (flags & HAS_CHILDREN_UPDATES) != 0) { + buf.append("\n\t"); + buf.append("fHasChildrenUpdates = "); + buf.append( toString(fHasChildrenUpdatesScheduled) ); + } + if ( (flags & CHILD_COUNT_UPDATES_STARTED) != 0) { + buf.append("\n\t"); + buf.append("fChildCountUpdatesRunning = "); + buf.append( toStringViewerUpdatesSet(fChildCountUpdatesRunning) ); + buf.append("\n\t"); + buf.append("fChildCountUpdatesCompleted = "); + buf.append( toStringViewerUpdatesSet(fChildCountUpdatesCompleted) ); + } + if ( (flags & CHILD_COUNT_UPDATES) != 0) { + buf.append("\n\t"); + buf.append("fChildCountUpdates = "); + buf.append( toString(fChildCountUpdatesScheduled) ); + } + if ( (flags & CHILDREN_UPDATES_STARTED) != 0) { + buf.append("\n\t"); + buf.append("fChildrenUpdatesRunning = "); + buf.append( fChildrenUpdatesRunning ); + buf.append("\n\t"); + buf.append("fChildrenUpdatesCompleted = "); + buf.append( toStringViewerUpdatesSet(fChildrenUpdatesCompleted) ); + } + if ( (flags & CHILDREN_UPDATES) != 0) { + buf.append("\n\t"); + buf.append("fChildrenUpdates = "); + buf.append( toStringTreePathMap(fChildrenUpdatesScheduled) ); + } + if ( (flags & MODEL_CHANGED_COMPLETE) != 0) { + buf.append("\n\t"); + buf.append("fModelChangedComplete = " + fModelChangedComplete); + } + if ( (flags & STATE_SAVE_COMPLETE) != 0) { + buf.append("\n\t"); + buf.append("fStateSaveComplete = " + fStateSaveComplete); + } + if ( (flags & STATE_RESTORE_COMPLETE) != 0) { + buf.append("\n\t"); + buf.append("fStateRestoreComplete = " + fStateRestoreComplete); + } +// if ( (flags & MODEL_PROXIES_INSTALLED) != 0) { +// buf.append("\n\t"); +// buf.append("fProxyModels = " + fProxyModels); +// } + if ( (flags & PROPERTY_UPDATES_STARTED) != 0) { + buf.append("\n\t"); + buf.append("fPropertiesUpdatesRunning = "); + buf.append(toStringViewerUpdatesSet(fPropertiesUpdatesRunning)); + buf.append("\n\t"); + buf.append("fPropertiesUpdatesCompleted = " + fPropertiesUpdatesCompleted); + } + if ( (flags & PROPERTY_UPDATES) != 0) { + buf.append("\n\t"); + buf.append("fPropertiesUpdates = "); + buf.append( toString(fPropertiesUpdates) ); + } + if (fTimeoutInterval > 0) { + buf.append("\n\t"); + buf.append("fTimeoutInterval = " + fTimeoutInterval); + } + return buf.toString(); + } + + private String toString(Set<TreePath> set) { + if (set.isEmpty()) { + return "(EMPTY)"; + } + StringBuffer buf = new StringBuffer(); + for (Iterator<TreePath> itr = set.iterator(); itr.hasNext(); ) { + buf.append("\n\t\t"); + buf.append(toStringTreePath(itr.next())); + } + return buf.toString(); + } + + private String toStringViewerUpdatesSet(Set<IViewerUpdate> set) { + if (set.isEmpty()) { + return "(EMPTY)"; + } + StringBuffer buf = new StringBuffer(); + for (Iterator<IViewerUpdate> itr = set.iterator(); itr.hasNext(); ) { + buf.append("\n\t\t"); + buf.append(toStringTreePath((itr.next()).getElementPath())); + } + return buf.toString(); + } + + private String toStringTreePathMap(Map<TreePath, Set<Integer>> map) { + if (map.isEmpty()) { + return "(EMPTY)"; + } + StringBuffer buf = new StringBuffer(); + for (Iterator<TreePath> itr = map.keySet().iterator(); itr.hasNext(); ) { + buf.append("\n\t\t"); + TreePath path = itr.next(); + buf.append(toStringTreePath(path)); + Set<?> updates = map.get(path); + buf.append(" = "); + buf.append(updates.toString()); + } + return buf.toString(); + } + + private String toStringTreePath(TreePath path) { + if (path.getSegmentCount() == 0) { + return "/"; + } + StringBuffer buf = new StringBuffer(); + for (int i = 0; i < path.getSegmentCount(); i++) { + buf.append("/"); + buf.append(path.getSegment(i)); + } + return buf.toString(); + } + + @Override + public String toString() { + return toString(ALL_UPDATES_COMPLETE | MODEL_CHANGED_COMPLETE | STATE_RESTORE_COMPLETE | + VIEWER_UPDATES_STARTED | LABEL_SEQUENCE_STARTED | PROPERTY_UPDATES | PROPERTY_UPDATES_STARTED); + } +} + + diff --git a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/debug/vm/launch/VMTest1.java b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/debug/vm/launch/VMTest1.java new file mode 100644 index 00000000000..fb2aa034d40 --- /dev/null +++ b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/debug/vm/launch/VMTest1.java @@ -0,0 +1,141 @@ +/******************************************************************************* + * Copyright (c) 2008 Wind River 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.tests.dsf.debug.vm.launch; + +import java.io.File; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.Query; +import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.launch.ILaunchVMConstants; +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.VMPropertiesUpdate; +import org.eclipse.cdt.examples.dsf.pda.PDAPlugin; +import org.eclipse.cdt.tests.dsf.IViewerUpdatesListenerConstants; +import org.eclipse.cdt.tests.dsf.vm.TestModelUpdatesListener; +import org.eclipse.core.runtime.Path; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelChangedListener; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDeltaVisitor; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxy; +import org.eclipse.debug.internal.ui.viewers.model.provisional.PresentationContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.VirtualTreeModelViewer; +import org.eclipse.debug.ui.IDebugUIConstants; +import org.eclipse.jface.viewers.TreePath; +import org.eclipse.swt.widgets.Display; +import org.junit.Assert; +import org.junit.Test; + +/** + * + */ +public class VMTest1 extends VMTestBase implements IViewerUpdatesListenerConstants { + + @Override + protected String getProgramPath() { + File programFile = PDAPlugin.getFileInPlugin(new Path("samples/example.pda")); + return programFile.getPath(); + } + + @Test + public void testRun() throws Throwable { + + Display display = Display.getDefault(); + + final VirtualTreeModelViewer dv = new VirtualTreeModelViewer( + display, 0, new PresentationContext(IDebugUIConstants.ID_DEBUG_VIEW)); + + TestModelUpdatesListener listener = new TestModelUpdatesListener(dv, false, false); + + // Wait for container expand delta, sent by the model upon DV install event. + final boolean[] containerExpandReceived = new boolean[1]; + containerExpandReceived[0] = false; + dv.addModelChangedListener(new IModelChangedListener() { + public void modelChanged(IModelDelta delta, IModelProxy proxy) { + delta.accept(new IModelDeltaVisitor() { + public boolean visit(IModelDelta delta, int depth) { + if (delta.getElement() instanceof IDMVMContext && + ((IDMVMContext)delta.getElement()).getDMContext() instanceof IContainerDMContext && + (delta.getFlags() & IModelDelta.EXPAND) != 0) + { + containerExpandReceived[0] = true; + return false; + } + return true; + } + }); + } + }); + + dv.setInput(DebugPlugin.getDefault().getLaunchManager()); + + while(!containerExpandReceived[0]) { + if (!display.readAndDispatch()) display.sleep(); + } + + listener.reset(); + + // TODO: need to wait for the install delta for the launch to be processed + while (!listener.isFinished(CONTENT_SEQUENCE_COMPLETE)) { + if (!display.readAndDispatch()) display.sleep(); + } + + // Find our launch + int launchIdx = dv.findElementIndex(TreePath.EMPTY, getLaunch()); + Assert.assertTrue(-1 != launchIdx); + + // Find the debug container + TreePath launchPath = TreePath.EMPTY.createChildPath(getLaunch()); + int launchChildCount = dv.getChildCount(launchPath); + IDMVMContext _containerVMC = null; + for (int i = 0; i < launchChildCount; i++) { + Object launchChild = dv.getChildElement(launchPath, i); + if (launchChild instanceof IDMVMContext && + ((IDMVMContext)launchChild).getDMContext() instanceof IContainerDMContext) + { + _containerVMC = (IDMVMContext)launchChild; + } + } + Assert.assertNotNull(_containerVMC); + final IDMVMContext containerVMC = _containerVMC; + final TreePath containerPath = launchPath.createChildPath(containerVMC); + final IElementPropertiesProvider containerPropProvider = + (IElementPropertiesProvider)containerVMC.getAdapter(IElementPropertiesProvider.class); + Assert.assertNotNull(containerPropProvider); + + // Check if container is suspended. + Query<Map<String,Object>> suspendedQuery = new Query<Map<String,Object>>() { + @Override + protected void execute(DataRequestMonitor<Map<String, Object>> rm) { + Set<String> properties = new HashSet<String>(); + properties.add(ILaunchVMConstants.PROP_IS_SUSPENDED); + + containerPropProvider.update( new VMPropertiesUpdate[] { + new VMPropertiesUpdate(properties, containerPath, dv.getInput(), dv.getPresentationContext(), rm) }); + } + }; + suspendedQuery.run(); + + // Wait for the properties update to complete + while (!suspendedQuery.isDone()) { + if (!display.readAndDispatch()) display.sleep(); + } + + Map<String,Object> properties = suspendedQuery.get(); + Assert.assertEquals(Boolean.TRUE, properties.get(ILaunchVMConstants.PROP_IS_SUSPENDED)); + + } +} diff --git a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/debug/vm/launch/VMTestBase.java b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/debug/vm/launch/VMTestBase.java new file mode 100644 index 00000000000..8bb546446fc --- /dev/null +++ b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/debug/vm/launch/VMTestBase.java @@ -0,0 +1,128 @@ +/******************************************************************************* + * Copyright (c) 2007 Ericsson 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: + * Ericsson - Initial Implementation + *******************************************************************************/ +package org.eclipse.cdt.tests.dsf.debug.vm.launch; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.cdt.dsf.debug.service.IRunControl.IStartedDMEvent; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.dsf.service.DsfSession.SessionStartedListener; +import org.eclipse.cdt.examples.dsf.pda.PDAPlugin; +import org.eclipse.cdt.examples.dsf.pda.launch.PDALaunch; +import org.eclipse.cdt.tests.dsf.ServiceEventWaitor; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.Path; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.core.ILaunchConfigurationType; +import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; +import org.eclipse.debug.core.ILaunchManager; +import org.eclipse.debug.internal.ui.DebugUIPlugin; +import org.eclipse.debug.internal.ui.IInternalDebugUIConstants; +import org.eclipse.jface.dialogs.MessageDialogWithToggle; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; + +/** + * This is the base class for the GDB/MI Unit tests. + * It provides the @Before and @After methods which setup + * and teardown the launch, for each test. + * If these methods are overwridden by a subclass, the new method + * must call super.baseSetup or super.baseTeardown itself, if this + * code is to be run. + */ +public class VMTestBase { + + private PDALaunch fLaunch; + public PDALaunch getPDALaunch() { return fLaunch; } + + @BeforeClass + public static void baseBeforeClassMethod() { + DebugUIPlugin.getDefault().getPreferenceStore().setValue( + IInternalDebugUIConstants.PREF_SWITCH_TO_PERSPECTIVE, MessageDialogWithToggle.NEVER); + DebugUIPlugin.getDefault().getPreferenceStore().setValue( + IInternalDebugUIConstants.PREF_SWITCH_PERSPECTIVE_ON_SUSPEND, MessageDialogWithToggle.NEVER); + } + + @Before + public void baseBeforeMethod() throws Exception { + Map<String, Object> attrs = new HashMap<String, Object>(); + + initLaunchAttributes(attrs); + + System.out.println("===================================================================="); + System.out.println("Launching test application: " + attrs.get(PDAPlugin.ATTR_PDA_PROGRAM)); + System.out.println("===================================================================="); + + ILaunchManager launchMgr = DebugPlugin.getDefault().getLaunchManager(); + ILaunchConfigurationType lcType = launchMgr.getLaunchConfigurationType("org.eclipse.cdt.examples.dsf.pda.launchType"); + assert lcType != null; + + ILaunchConfigurationWorkingCopy lcWorkingCopy = lcType.newInstance( + null, + launchMgr.generateUniqueLaunchConfigurationNameFrom("Test Launch")); //$NON-NLS-1$ + assert lcWorkingCopy != null; + lcWorkingCopy.setAttributes(attrs); + + final ILaunchConfiguration lc = lcWorkingCopy.doSave(); + assert lc != null; + + final ServiceEventWaitor<?> eventWaitor[] = new ServiceEventWaitor<?>[1]; + + SessionStartedListener newSessionListener = new SessionStartedListener() { + public void sessionStarted(DsfSession session) { + eventWaitor[0] = new ServiceEventWaitor<IStartedDMEvent>(session, IStartedDMEvent.class); + } + }; + + DsfSession.addSessionStartedListener(newSessionListener); + try { + fLaunch = (PDALaunch)lc.launch(ILaunchManager.DEBUG_MODE, new NullProgressMonitor()); + Assert.assertNotNull(fLaunch); + Assert.assertNotNull(eventWaitor[0]); + Assert.assertSame(fLaunch.getSession(), eventWaitor[0].getSession()); + eventWaitor[0].waitForEvent(60000); + } finally { + DsfSession.removeSessionStartedListener(newSessionListener); + if (eventWaitor[0] != null) { + eventWaitor[0].dispose(); + } + } + } + + protected void initLaunchAttributes(Map<String, Object> attrs) { + attrs.put(PDAPlugin.ATTR_PDA_PROGRAM, getProgramPath()); + } + + protected String getProgramPath() { + File programFile = PDAPlugin.getFileInPlugin(new Path("samples/example.pda")); + return programFile.getPath(); + } + + protected ILaunch getLaunch() { + return fLaunch; + } + + @After + public void baseAfterMethod() throws Exception { + if (fLaunch != null) { + fLaunch.terminate(); + fLaunch = null; + } + + } + +} diff --git a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/DummyFormattedValueService.java b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/DummyFormattedValueService.java new file mode 100644 index 00000000000..ae105165c4f --- /dev/null +++ b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/DummyFormattedValueService.java @@ -0,0 +1,58 @@ +package org.eclipse.cdt.tests.dsf.vm; + +import java.util.Hashtable; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues; +import org.eclipse.cdt.dsf.service.AbstractDsfService; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.tests.dsf.DsfTestPlugin; +import org.osgi.framework.BundleContext; + +public class DummyFormattedValueService extends AbstractDsfService implements IFormattedValues { + + public static String DUMMY_FORMAT = "dummy"; + public static String[] AVAILABLE_FORMATS = new String[] { DUMMY_FORMAT, HEX_FORMAT, OCTAL_FORMAT, BINARY_FORMAT, NATURAL_FORMAT, DECIMAL_FORMAT, STRING_FORMAT }; + + public DummyFormattedValueService(DsfSession session) { + super(session); + } + + @Override + public void initialize(RequestMonitor rm) { + super.initialize(new RequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + register(new String[0], new Hashtable<String, String>() ); + super.handleSuccess(); + } + }); + } + + @Override + public void shutdown(RequestMonitor rm) { + unregister(); + super.shutdown(rm); + } + + @Override + protected BundleContext getBundleContext() { + return DsfTestPlugin.getBundleContext(); + } + + public void getAvailableFormats(IFormattedDataDMContext dmc, DataRequestMonitor<String[]> rm) { + rm.setData(AVAILABLE_FORMATS); + rm.done(); + } + + public FormattedValueDMContext getFormattedValueContext(IFormattedDataDMContext dmc, String formatId) { + return new FormattedValueDMContext(this, dmc, formatId); + } + + public void getFormattedExpressionValue(FormattedValueDMContext dmc, DataRequestMonitor<FormattedValueDMData> rm) { + rm.setData(new FormattedValueDMData(dmc.getFormatID())); + rm.done(); + } + +}
\ No newline at end of file diff --git a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/FormattedValueTests.java b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/FormattedValueTests.java new file mode 100644 index 00000000000..60d127311a7 --- /dev/null +++ b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/FormattedValueTests.java @@ -0,0 +1,372 @@ +/******************************************************************************* + * Copyright (c) 2009, 2010 Wind River 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.tests.dsf.vm; + +import java.util.concurrent.ExecutionException; + +import junit.framework.Assert; +import junit.framework.AssertionFailedError; +import junit.framework.TestCase; + +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DefaultDsfExecutor; +import org.eclipse.cdt.dsf.concurrent.DsfExecutor; +import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; +import org.eclipse.cdt.dsf.concurrent.Query; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.IDebugVMConstants; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.numberformat.FormattedValueVMUtil; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.dsf.service.IDsfService; +import org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMAdapter; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMProvider; +import org.eclipse.cdt.dsf.ui.viewmodel.properties.IPropertiesUpdate; +import org.eclipse.cdt.dsf.ui.viewmodel.properties.PropertiesUpdateStatus; +import org.eclipse.cdt.dsf.ui.viewmodel.update.IVMUpdatePolicy; +import org.eclipse.cdt.dsf.ui.viewmodel.update.ManualUpdatePolicy; +import org.eclipse.cdt.tests.dsf.IViewerUpdatesListenerConstants; +import org.eclipse.cdt.tests.dsf.vm.TestModel.TestElement; +import org.eclipse.cdt.tests.dsf.vm.TestModel.TestElementValidator; +import org.eclipse.cdt.tests.dsf.vm.TestModel.TestEvent; +import org.eclipse.debug.internal.ui.viewers.model.ITreeModelContentProviderTarget; +import org.eclipse.debug.internal.ui.viewers.model.ITreeModelViewer; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.jface.viewers.TreePath; +import org.eclipse.jface.viewers.ViewerLabel; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.PlatformUI; + +/** + * Tests to verify the operation of FormattedValuesVMUtil + * @since 2.2 + */ +abstract public class FormattedValueTests extends TestCase implements IViewerUpdatesListenerConstants, IDebugVMConstants { + + Display fDisplay; + Shell fShell; + DsfExecutor fDsfExecutor; + DsfSession fDsfSession; + ITreeModelViewer fViewer; + TestModelUpdatesListener fViewerListener; + TestModelUpdatesListener fVMListener; + FormattedValuesListener fFormattedValuesListener; + TestModel fModel; + DummyFormattedValueService fDummyValuesService; + AbstractVMAdapter fVMAdapter; + TestModelCachingVMProvider fVMProvider; + + public FormattedValueTests(String name) { + super(name); + } + + /** + * @throws java.lang.Exception + */ + @Override + protected void setUp() throws Exception { + fDsfExecutor = new DefaultDsfExecutor(); + fDsfSession = DsfSession.startSession(fDsfExecutor, getClass().getName()); + + fDisplay = PlatformUI.getWorkbench().getDisplay(); + fShell = new Shell(fDisplay/*, SWT.ON_TOP | SWT.SHELL_TRIM*/); + fShell.setMaximized(true); + fShell.setLayout(new FillLayout()); + + fViewer = createViewer(fDisplay, fShell); + + fModel = new TestModel(fDsfSession); + initializeService(fModel); + fDummyValuesService = new DummyFormattedValueService(fDsfSession); + initializeService(fDummyValuesService); + + fViewerListener = new TestModelUpdatesListener(fViewer, true, false); + + fModel.setRoot( new TestElement(fModel, "root", new TestElement[0] ) ); + fModel.setElementChildren(TreePath.EMPTY, makeModelElements(fModel, getTestModelDepth(), "model")); + + fVMAdapter = new AbstractVMAdapter() { + @Override + protected IVMProvider createViewModelProvider(IPresentationContext context) { + return fVMProvider; + } + }; + fVMProvider = new TestModelCachingVMProvider(fVMAdapter, fViewer.getPresentationContext(), fDsfSession); + + fVMListener = new TestModelUpdatesListener(); + fVMProvider.getNode().setVMUpdateListener(fVMListener); + fVMProvider.getNode().getLabelProvider().addPropertiesUpdateListener(fViewerListener); + + fFormattedValuesListener = new FormattedValuesListener(fModel); + fVMProvider.getNode().setFormattedValuesListener(fFormattedValuesListener); + fModel.setTestModelListener(fFormattedValuesListener); + + fShell.open (); + } + + private void initializeService(final IDsfService service) throws InterruptedException, ExecutionException { + Query<Object> initQuery = new Query<Object>() { + @Override + protected void execute(DataRequestMonitor<Object> rm) { + rm.setData(new Object()); + service.initialize(rm); + } + }; + fDsfExecutor.execute(initQuery); + initQuery.get(); + } + + abstract protected ITreeModelContentProviderTarget createViewer(Display display, Shell shell); + + /** + * @throws java.lang.Exception + */ + @Override + protected void tearDown() throws Exception { + fVMProvider.getNode().setFormattedValuesListener(null); + fModel.setTestModelListener(null); + + fVMProvider.getNode().getLabelProvider().removePropertiesUpdateListener(fViewerListener); + fVMProvider.getNode().setVMUpdateListener(null); + + fVMAdapter.dispose(); + + fVMListener.dispose(); + fViewerListener.dispose(); + + shutdownService(fDummyValuesService); + shutdownService(fModel); + fViewer.getPresentationContext().dispose(); + // Close the shell and exit. + fShell.close(); + while (!fShell.isDisposed()) if (!fDisplay.readAndDispatch ()) fDisplay.sleep (); + DsfSession.endSession(fDsfSession); + fDsfExecutor.shutdown(); + } + + private void shutdownService(final IDsfService service) throws InterruptedException, ExecutionException { + Query<Object> shutdownQuery = new Query<Object>() { + @Override + protected void execute(DataRequestMonitor<Object> rm) { + rm.setData(new Object()); + service.shutdown(rm); + } + }; + fDsfExecutor.execute(shutdownQuery); + shutdownQuery.get(); + } + + /** + * Depth (size) of the test model to be used in the tests. This number allows + * the jface based tests to use a small enough model to fit on the screen, and + * for the virtual viewer to exercise the content provider to a greater extent. + */ + abstract protected int getTestModelDepth(); + + public void testValidate() { + setInput(IFormattedValues.NATURAL_FORMAT); + setFormatAndValidate(IFormattedValues.HEX_FORMAT, false, false, false); + } + + public void testChangeFormat() { + setInput(IFormattedValues.NATURAL_FORMAT); + setFormatAndValidate(IFormattedValues.HEX_FORMAT, false, false, false); + setFormatAndValidate(IFormattedValues.NATURAL_FORMAT, false, false, false); + } + + public void testChangeFormatManualUpdateMode() { + setInput(IFormattedValues.NATURAL_FORMAT); + setUpdatePolicy(ManualUpdatePolicy.MANUAL_UPDATE_POLICY_ID); + + // Chenge to a new format, this does not cause the cache entries to be + // set to dirty. Retrieving new format values should happen from the service. + setFormatAndValidate(IFormattedValues.HEX_FORMAT, true, false, false); + + // Change _back_ to natural format. Values should be retrieved from cache. + setFormatAndValidate(IFormattedValues.NATURAL_FORMAT, true, true, false); + + // Generate an event which will cause all cache entries to be marked dirty. + postEventInManualUpdateMode(); + + // Change back again to hex format. Values should be retrieved from cache. + setFormatAndValidate(IFormattedValues.HEX_FORMAT, true, true, false); + + // Change to a decimal, which is not cached, values should come with an error. + setFormatAndValidate(IFormattedValues.DECIMAL_FORMAT, true, true, true); + + } + + private void postEventInManualUpdateMode() { + // Generate an event which will cause all cache entries to be marked dirty. + fViewerListener.reset(); + fViewerListener.addUpdates(TreePath.EMPTY, fModel.getRootElement(), -1, ALL_UPDATES_COMPLETE | PROPERTY_UPDATES); + fVMListener.reset(); + fFormattedValuesListener.reset(); + fVMProvider.postEvent(new TestEvent(fModel.getRootElement(), IModelDelta.CONTENT)); + while (!fViewerListener.isFinished(ALL_UPDATES_COMPLETE | PROPERTY_UPDATES)) + if (!fDisplay.readAndDispatch ()) fDisplay.sleep (); + Assert.assertTrue(fFormattedValuesListener.getFormattedValuesCompleted().isEmpty()); + } + + public void testInvalidFormat() { + setInput(IFormattedValues.NATURAL_FORMAT); + + fViewerListener.reset(); + fViewerListener.addUpdates(TreePath.EMPTY, ((TestElementVMContext)fViewer.getInput()).getElement(), -1, ALL_UPDATES_COMPLETE | PROPERTY_UPDATES); + + fVMListener.reset(); + fVMListener.addUpdates(TreePath.EMPTY, fModel.getRootElement(), -1, ALL_UPDATES_COMPLETE | PROPERTY_UPDATES); + + // Set the new number format to the viewer. + fViewer.getPresentationContext().setProperty(PROP_FORMATTED_VALUE_FORMAT_PREFERENCE, "invalid format"); + + while (!fViewerListener.isFinished(ALL_UPDATES_COMPLETE | PROPERTY_UPDATES) || !fVMListener.isFinished(CONTENT_UPDATES | PROPERTY_UPDATES)) + if (!fDisplay.readAndDispatch ()) fDisplay.sleep (); + + validateModel(IFormattedValues.HEX_FORMAT, " (" + FormattedValueVMUtil.getFormatLabel(IFormattedValues.HEX_FORMAT) + ")"); + } + + /** + * Initial format is NATURAL. + */ + private void setInput(String formatId) { + // Set the new number format to the viewer. + fViewer.getPresentationContext().setProperty(PROP_FORMATTED_VALUE_FORMAT_PREFERENCE, formatId); + + fViewer.setAutoExpandLevel(-1); + TestElementVMContext rootVMC = fVMProvider.getElementVMContext(fViewer.getPresentationContext(), fModel.getRootElement()); + + // Create the listener + fViewerListener.reset(); + fViewerListener.addUpdates(TreePath.EMPTY, rootVMC.getElement(), -1, ALL_UPDATES_COMPLETE | PROPERTY_UPDATES); + fVMListener.reset(); + fVMListener.addUpdates(TreePath.EMPTY, rootVMC.getElement(), -1, ALL_UPDATES_COMPLETE | PROPERTY_UPDATES); + fFormattedValuesListener.reset(); + + fViewer.setInput(rootVMC); + while (!fViewerListener.isFinished(ALL_UPDATES_COMPLETE | PROPERTY_UPDATES) || !fVMListener.isFinished(CONTENT_COMPLETE | PROPERTY_UPDATES)) + if (!fDisplay.readAndDispatch ()) fDisplay.sleep (); + + Assert.assertTrue(fFormattedValuesListener.isFinished()); + } + + private void setUpdatePolicy(String policyId) { + IVMUpdatePolicy[] policies = fVMProvider.getAvailableUpdatePolicies(); + IVMUpdatePolicy newPolicy = null; + for (IVMUpdatePolicy policy : policies) { + if (policyId.equals(policy.getID())) { + newPolicy = policy; + break; + } + } + if (newPolicy != null) { + fVMProvider.setActiveUpdatePolicy(newPolicy); + } else { + throw new RuntimeException("Update policy " + policyId + " not available"); + } + fViewerListener.reset(); + fViewerListener.addUpdates(TreePath.EMPTY, fModel.getRootElement(), -1, ALL_UPDATES_COMPLETE | PROPERTY_UPDATES); + fVMListener.setFailOnRedundantUpdates(false); + while (!fViewerListener.isFinished(ALL_UPDATES_COMPLETE | PROPERTY_UPDATES)) + if (!fDisplay.readAndDispatch ()) fDisplay.sleep (); + + fVMListener.setFailOnRedundantUpdates(true); + } + + private void setFormatAndValidate( + String formatId, + boolean expectContentCached, + boolean expectFormattedValuesCached, + boolean expectCacheMissError) + { + fViewerListener.reset(); + fViewerListener.addUpdates(TreePath.EMPTY, ((TestElementVMContext)fViewer.getInput()).getElement(), -1, ALL_UPDATES_COMPLETE | PROPERTY_UPDATES); + + fVMListener.reset(); + int vmUpdateFlags = PROPERTY_UPDATES; + if (!expectContentCached) { + vmUpdateFlags |= ALL_UPDATES_COMPLETE; + } + fVMListener.addUpdates(TreePath.EMPTY, fModel.getRootElement(), -1, vmUpdateFlags); + + fFormattedValuesListener.reset(); + if (expectFormattedValuesCached && !expectCacheMissError) { + fFormattedValuesListener.setCachedFormats(new String[] {formatId} ); + } + + // Set the new number format to the viewer. + fViewer.getPresentationContext().setProperty(PROP_FORMATTED_VALUE_FORMAT_PREFERENCE, formatId); + + while (!fViewerListener.isFinished(ALL_UPDATES_COMPLETE | PROPERTY_UPDATES) || !fVMListener.isFinished(CONTENT_UPDATES | PROPERTY_UPDATES)) + if (!fDisplay.readAndDispatch ()) fDisplay.sleep (); + + if (expectCacheMissError) { + try { + validateModel(formatId, ""); + throw new RuntimeException("Expected validateModel to fail"); + } + catch(AssertionFailedError e) { + // expected + } + } else { + validateModel(formatId, ""); + } + + if (expectCacheMissError) { + String formatProperty = FormattedValueVMUtil.getPropertyForFormatId(formatId); + + Assert.assertTrue(fFormattedValuesListener.getFormattedValuesCompleted().isEmpty()); + Assert.assertFalse(fFormattedValuesListener.getPropertiesUpdates().isEmpty()); + for (IPropertiesUpdate update : fFormattedValuesListener.getPropertiesUpdates()) { + PropertiesUpdateStatus status = (PropertiesUpdateStatus)update.getStatus(); + assertEquals(IDsfStatusConstants.INVALID_STATE, status.getCode()); + assertEquals("Cache contains stale data. Refresh view.", status.getStatus(formatProperty).getMessage()); + assertEquals( + "Cache contains stale data. Refresh view.", + status.getStatus(PROP_FORMATTED_VALUE_ACTIVE_FORMAT_VALUE).getMessage()); + assertEquals(1, status.getChildren().length); + + } + + } else { + Assert.assertTrue(fFormattedValuesListener.isFinished()); + } + + } + + private void validateModel(final String formatId, final String suffix) { + fModel.validateData( + fViewer, TreePath.EMPTY, + new TestElementValidator() { + public void validate(TestElement modelElement, TestElement viewerElement, TreePath viewerPath) { + ViewerLabel label = fViewer.getElementLabel(viewerPath, TestModelCachingVMProvider.COLUMN_ID); + assertEquals(modelElement.getID(), label.getText()); + + label = fViewer.getElementLabel(viewerPath, TestModelCachingVMProvider.COLUMN_FORMATTED_VALUE); + assertEquals(fModel.getFormattedValueText(modelElement, formatId) + suffix, label.getText()); + + label = fViewer.getElementLabel(viewerPath, TestModelCachingVMProvider.COLUMN_DUMMY_VALUE); + assertEquals(formatId, label.getText()); + } + }); + } + + private TestElement[] makeModelElements(TestModel model, int depth, String prefix) { + TestElement[] elements = new TestElement[depth]; + for (int i = 0; i < depth; i++) { + String name = prefix + "." + i; + elements[i] = new TestElement(model, name, makeModelElements(model, i, name)); + } + return elements; + } +} diff --git a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/FormattedValuesListener.java b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/FormattedValuesListener.java new file mode 100644 index 00000000000..cb87bde7334 --- /dev/null +++ b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/FormattedValuesListener.java @@ -0,0 +1,200 @@ +/******************************************************************************* + * Copyright (c) 2010 Wind River 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.tests.dsf.vm; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import org.eclipse.cdt.dsf.concurrent.DsfRunnable; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMContext; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.IDebugVMConstants; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.numberformat.FormattedValueVMUtil; +import org.eclipse.cdt.dsf.ui.viewmodel.properties.IPropertiesUpdate; +import org.eclipse.cdt.dsf.ui.viewmodel.properties.IPropertiesUpdateListener; +import org.eclipse.cdt.tests.dsf.vm.TestModel.TestElement; +import org.junit.Assert; + +/** + * + */ +public class FormattedValuesListener implements IFormattedValuesListener, IPropertiesUpdateListener { + + private static final String ANY_FORMAT = "ANY_FORMAT"; + + private final TestModel fModel; + + private List<IPropertiesUpdate> fPropertiesUpdates = new ArrayList<IPropertiesUpdate>(); + private List<List<FormattedValueDMContext>> fFormattedValuesInPending = new ArrayList<List<FormattedValueDMContext>>(); + private List<FormattedValueDMContext> fFormattedValuesInProgress = new LinkedList<FormattedValueDMContext>(); + private List<FormattedValueDMContext> fFormattedValuesCompleted = new ArrayList<FormattedValueDMContext>(); + + private DsfRunnable fProcessUpdatedFormattedValuesRunnable = null; + + private Set<String> fCachedFormats = new HashSet<String>(); + + public FormattedValuesListener(TestModel model) { + fModel = model; + } + + public void setCachedFormats(String[] cachedFormats) { + fCachedFormats.clear(); + fCachedFormats.addAll(Arrays.asList(cachedFormats)); + } + + public void propertiesUpdatesStarted(IPropertiesUpdate[] updates) { + fPropertiesUpdates.addAll(Arrays.asList(updates)); + List<FormattedValueDMContext> pending = new ArrayList<FormattedValueDMContext>(updates.length); + for (IPropertiesUpdate update : updates) { + List<String> formatIds = getRequestedFormatIDs(update); + for (String formatId : formatIds) { + TestElement te = getPropertyUpdateTestElement(update); + pending.add(new FormattedValueDMContext(fModel, te, formatId)); + } + } + if (!pending.isEmpty()) { + fFormattedValuesInPending.add(pending); + } + } + + private List<String> getRequestedFormatIDs(IPropertiesUpdate update) { + List<String> formatIds = new ArrayList<String>(1); + for (String property : update.getProperties()) { + if (property.equals(IDebugVMConstants.PROP_FORMATTED_VALUE_ACTIVE_FORMAT_VALUE)) { + formatIds.add(ANY_FORMAT); + } + if (property.startsWith(IDebugVMConstants.PROP_FORMATTED_VALUE_BASE)) { + formatIds.add(FormattedValueVMUtil.getFormatFromProperty(property, null)); + } + } + return formatIds; + } + + public void reset() { + reset(new String[0]); + } + + public void reset(String[] cachedFormats) { + fPropertiesUpdates.clear(); + fFormattedValuesInPending.clear(); + fFormattedValuesInProgress.clear(); + fFormattedValuesCompleted.clear(); + setCachedFormats(cachedFormats); + } + + public List<FormattedValueDMContext> getFormattedValuesCompleted() { + return fFormattedValuesCompleted; + } + + public List<IPropertiesUpdate> getPropertiesUpdates() { + return fPropertiesUpdates; + } + + public boolean isFinished() { + if ( !fFormattedValuesInProgress.isEmpty() ) { + return false; + } + + if (!fFormattedValuesInPending.isEmpty() && fCachedFormats.isEmpty()) { + return false; + } + + for (List<FormattedValueDMContext> pendingList : fFormattedValuesInPending) { + for (FormattedValueDMContext pending : pendingList) { + String pendingFormat = pending.getFormatID(); + if (!pendingFormat.equals(ANY_FORMAT) && !fCachedFormats.contains(pendingFormat)) { + return false; + } + } + } + return true; + } + + public void propertiesUpdateCompleted(IPropertiesUpdate update) {} + + public void formattedValueUpdated(FormattedValueDMContext formattedValueDmc) { + Assert.assertFalse("Expected values with formats " + fCachedFormats + " to be cached.", + fCachedFormats.contains(formattedValueDmc.getFormatID())); + + if (fProcessUpdatedFormattedValuesRunnable == null) { + fProcessUpdatedFormattedValuesRunnable = new DsfRunnable() { + public void run() { + fProcessUpdatedFormattedValuesRunnable = null; + processFormattedValuesInProgress(); + } + }; + fModel.getExecutor().execute(fProcessUpdatedFormattedValuesRunnable); + } + fFormattedValuesInProgress.add(formattedValueDmc); + } + + private void processFormattedValuesInProgress() { + while (!fFormattedValuesInProgress.isEmpty()) { + List<FormattedValueDMContext> pendingList = findPendingList(fFormattedValuesInProgress.get(0)); + + for (FormattedValueDMContext pending : pendingList) { + int progressIdx = indexOfFormattedValueDMContext(fFormattedValuesInProgress, pending); + + if (progressIdx != -1) { + // The pending DMC may contain the ANY_FORMAT format ID. + // The progress DMC must contain the exact format retrieved. + // To have a more accurate record, add the progress DMC to + // the completed updates list. + FormattedValueDMContext progress = fFormattedValuesInProgress.remove(progressIdx); + fFormattedValuesCompleted.add(progress); + } else { + Assert.fail("Pending Updates not processed in bulk \n " + pendingList); + } + } + } + } + + private List<FormattedValueDMContext> findPendingList(FormattedValueDMContext dmc) { + for (Iterator<List<FormattedValueDMContext>> itr = fFormattedValuesInPending.iterator(); itr.hasNext();) { + List<FormattedValueDMContext> pendingList = itr.next(); + int pendingIdx = indexOfFormattedValueDMContext(pendingList, dmc); + if (pendingIdx != -1) { + itr.remove(); + return pendingList; + } + } + throw new RuntimeException("Pending update not found for element: " + dmc); + } + + private int indexOfFormattedValueDMContext(List<FormattedValueDMContext> list, FormattedValueDMContext dmc) { + for (int i = 0; i < list.size(); i++) { + if (dmc.getParentValueDMContext().equals(list.get(i).getParentValueDMContext())) { + if ( ANY_FORMAT.equals(dmc.getFormatID()) || + ANY_FORMAT.equals(list.get(i).getFormatID()) || + dmc.getFormatID().equals(list.get(i).getFormatID()) ) + { + return i; + } + } + } + return -1; + } + + private TestElement getPropertyUpdateTestElement(IPropertiesUpdate update) { + Object element = update.getElement(); + if (element instanceof TestElement) { + return (TestElement)element; + } else if (element instanceof TestElementVMContext) { + return ((TestElementVMContext)element).getElement(); + } + throw new RuntimeException("Invalid element in properties update: " + update.getElement()); + } + +} diff --git a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/IFormattedValuesListener.java b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/IFormattedValuesListener.java new file mode 100644 index 00000000000..ce919f87603 --- /dev/null +++ b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/IFormattedValuesListener.java @@ -0,0 +1,22 @@ +/******************************************************************************* + * Copyright (c) 2010 Wind River 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.tests.dsf.vm; + +import org.eclipse.cdt.dsf.debug.service.IFormattedValues.FormattedValueDMContext; + +/** + * @since 2.2 + */ +public interface IFormattedValuesListener { + + public void formattedValueUpdated(FormattedValueDMContext formattedValueDmc); + +} diff --git a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/ITestModelUpdatesListenerConstants.java b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/ITestModelUpdatesListenerConstants.java deleted file mode 100644 index 162d955e681..00000000000 --- a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/ITestModelUpdatesListenerConstants.java +++ /dev/null @@ -1,40 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2010 Wind River 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: - * Wind River Systems - initial API and implementation - *******************************************************************************/ -package org.eclipse.cdt.tests.dsf.vm; - -/** - * Convenience interface with constants used by the test model update listener. - * - * @since 3.6 - */ -public interface ITestModelUpdatesListenerConstants { - - public static final int LABEL_UPDATES_COMPLETE = 0X0001; - public static final int CONTENT_UPDATES_COMPLETE = 0X0002; - public static final int LABEL_UPDATES = 0X0004; - public static final int HAS_CHILDREN_UPDATES = 0X0008; - public static final int CHILDREN_COUNT_UPDATES = 0X0010; - public static final int CHILDREN_UPDATES = 0X0020; - public static final int MODEL_CHANGED_COMPLETE = 0X0040; - public static final int MODEL_PROXIES_INSTALLED = 0X0080; - public static final int STATE_SAVE_COMPLETE = 0X0100; - public static final int STATE_RESTORE_COMPLETE = 0X0200; - public static final int STATE_UPDATES = 0X0400; - - public static final int VIEWER_UPDATES_RUNNING = 0X0800; - public static final int LABEL_UPDATES_RUNNING = 0X1000; - - public static final int LABEL_COMPLETE = LABEL_UPDATES_COMPLETE | LABEL_UPDATES; - public static final int CONTENT_COMPLETE = - CONTENT_UPDATES_COMPLETE | HAS_CHILDREN_UPDATES | CHILDREN_COUNT_UPDATES | CHILDREN_UPDATES; - - public static final int ALL_UPDATES_COMPLETE = LABEL_COMPLETE | CONTENT_COMPLETE | LABEL_UPDATES_RUNNING | VIEWER_UPDATES_RUNNING; -} diff --git a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/JFaceViewerFormattedValueTests.java b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/JFaceViewerFormattedValueTests.java new file mode 100644 index 00000000000..c28a1b2d0be --- /dev/null +++ b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/JFaceViewerFormattedValueTests.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2009, 2010 Wind River 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.tests.dsf.vm; + +import org.eclipse.debug.internal.ui.viewers.model.ITreeModelContentProviderTarget; +import org.eclipse.debug.internal.ui.viewers.model.provisional.PresentationContext; +import org.eclipse.debug.internal.ui.viewers.model.provisional.TreeModelViewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; + +/** + * @since 2.2 + */ +public class JFaceViewerFormattedValueTests extends FormattedValueTests { + + public JFaceViewerFormattedValueTests(String name) { + super(name); + } + + @Override + protected ITreeModelContentProviderTarget createViewer(Display display, Shell shell) { + return new TreeModelViewer(fShell, SWT.VIRTUAL, new PresentationContext("TestViewer")); + } + + @Override + protected int getTestModelDepth() { + return 5; + } +} diff --git a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/PerformanceTests.java b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/PerformanceTests.java index 73c1cf543ec..3438d076bcb 100644 --- a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/PerformanceTests.java +++ b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/PerformanceTests.java @@ -12,6 +12,10 @@ package org.eclipse.cdt.tests.dsf.vm; import junit.framework.TestCase; +import org.eclipse.cdt.dsf.concurrent.DefaultDsfExecutor; +import org.eclipse.cdt.dsf.concurrent.DsfExecutor; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.tests.dsf.IViewerUpdatesListenerConstants; import org.eclipse.cdt.tests.dsf.vm.TestModel.TestElement; import org.eclipse.debug.internal.ui.viewers.model.ITreeModelContentProviderTarget; import org.eclipse.debug.internal.ui.viewers.model.ITreeModelViewer; @@ -28,9 +32,11 @@ import org.eclipse.ui.PlatformUI; /** * Tests to measure the performance of the viewer updates. */ -abstract public class PerformanceTests extends TestCase implements ITestModelUpdatesListenerConstants { +abstract public class PerformanceTests extends TestCase implements IViewerUpdatesListenerConstants { Display fDisplay; Shell fShell; + DsfExecutor fDsfExecutor; + DsfSession fDsfSession; ITreeModelViewer fViewer; TestModelUpdatesListener fListener; TestModel fModel; @@ -46,6 +52,9 @@ abstract public class PerformanceTests extends TestCase implements ITestModelUpd */ @Override protected void setUp() throws Exception { + fDsfExecutor = new DefaultDsfExecutor(); + fDsfSession = DsfSession.startSession(fDsfExecutor, getClass().getName()); + fDisplay = PlatformUI.getWorkbench().getDisplay(); fShell = new Shell(fDisplay/*, SWT.ON_TOP | SWT.SHELL_TRIM*/); fShell.setMaximized(true); @@ -55,7 +64,7 @@ abstract public class PerformanceTests extends TestCase implements ITestModelUpd fListener = new TestModelUpdatesListener(fViewer, false, false); - fModel = new TestModel(); + fModel = new TestModel(fDsfSession); fModel.setRoot( new TestElement(fModel, "root", new TestElement[0] ) ); fModel.setElementChildren(TreePath.EMPTY, makeModelElements(fModel, getTestModelDepth(), "model")); fVMAdapter = new TestModelVMAdapter(); @@ -78,6 +87,8 @@ abstract public class PerformanceTests extends TestCase implements ITestModelUpd // Close the shell and exit. fShell.close(); while (!fShell.isDisposed()) if (!fDisplay.readAndDispatch ()) fDisplay.sleep (); + DsfSession.endSession(fDsfSession); + fDsfExecutor.shutdown(); } /** @@ -166,32 +177,31 @@ abstract public class PerformanceTests extends TestCase implements ITestModelUpd } public void _x_testRefreshStructReplaceElements() { - TestModel model = new TestModel(); - model.setRoot( new TestElement(model, "root", new TestElement[0] ) ); - model.setElementChildren(TreePath.EMPTY, makeModelElements(model, getTestModelDepth(), "model")); + fModel.setRoot( new TestElement(fModel, "root", new TestElement[0] ) ); + fModel.setElementChildren(TreePath.EMPTY, makeModelElements(fModel, getTestModelDepth(), "model")); fViewer.setAutoExpandLevel(-1); // Create the listener - fListener.reset(TreePath.EMPTY, model.getRootElement(), -1, true, false); + fListener.reset(TreePath.EMPTY, fModel.getRootElement(), -1, true, false); // Set the input into the view and update the view. - fViewer.setInput(model.getRootElement()); + fViewer.setInput(fModel.getRootElement()); while (!fListener.isFinished()) if (!fDisplay.readAndDispatch ()) fDisplay.sleep (); - model.validateData(fViewer, TreePath.EMPTY); + fModel.validateData(fViewer, TreePath.EMPTY); Performance perf = Performance.getDefault(); PerformanceMeter meter = perf.createPerformanceMeter(perf.getDefaultScenarioId(this)); try { for (int i = 0; i < 2000; i++) { // Update the model - model.setElementChildren(TreePath.EMPTY, makeModelElements(model, getTestModelDepth(), "pass " + i)); + fModel.setElementChildren(TreePath.EMPTY, makeModelElements(fModel, getTestModelDepth(), "pass " + i)); - TestElement element = model.getRootElement(); + TestElement element = fModel.getRootElement(); fListener.reset(TreePath.EMPTY, element, -1, false, false); meter.start(); - model.postDelta(new ModelDelta(element, IModelDelta.CONTENT)); + //fModel.postDelta(new ModelDelta(element, IModelDelta.CONTENT)); while (!fListener.isFinished(ALL_UPDATES_COMPLETE | MODEL_CHANGED_COMPLETE)) if (!fDisplay.readAndDispatch ()) fDisplay.sleep (); //model.validateData(fViewer, TreePath.EMPTY); diff --git a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/TestElementVMContext.java b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/TestElementVMContext.java index 3d7360975df..e6ec468cc85 100644 --- a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/TestElementVMContext.java +++ b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/TestElementVMContext.java @@ -10,17 +10,20 @@ *******************************************************************************/ package org.eclipse.cdt.tests.dsf.vm; +import org.eclipse.cdt.dsf.datamodel.IDMContext; import org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMContext; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMNode; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.IDMVMContext; import org.eclipse.cdt.tests.dsf.vm.TestModel.TestElement; /** - * + * @since 2.2 */ -public class TestElementVMContext extends AbstractVMContext { +public class TestElementVMContext extends AbstractVMContext implements IDMVMContext { final private TestElement fElement; - public TestElementVMContext(TestModelVMNode node, TestElement element) { + public TestElementVMContext(IVMNode node, TestElement element) { super(node); fElement = element; } @@ -38,5 +41,13 @@ public class TestElementVMContext extends AbstractVMContext { public TestElement getElement() { return fElement; } + + public IDMContext getDMContext() { + return getElement(); + } + @Override + public String toString() { + return getDMContext().toString(); + } } diff --git a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/TestModel.java b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/TestModel.java index cbae0a9d6f7..da475627ad1 100644 --- a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/TestModel.java +++ b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/TestModel.java @@ -11,38 +11,45 @@ package org.eclipse.cdt.tests.dsf.vm; import java.util.Arrays; +import java.util.Hashtable; import junit.framework.Assert; -import org.eclipse.core.runtime.PlatformObject; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.datamodel.AbstractDMContext; +import org.eclipse.cdt.dsf.datamodel.DMContexts; +import org.eclipse.cdt.dsf.datamodel.IDMContext; +import org.eclipse.cdt.dsf.debug.service.IFormattedValues; +import org.eclipse.cdt.dsf.service.AbstractDsfService; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.tests.dsf.DsfTestPlugin; import org.eclipse.debug.internal.ui.viewers.model.ITreeModelCheckProviderTarget; import org.eclipse.debug.internal.ui.viewers.model.ITreeModelContentProviderTarget; import org.eclipse.debug.internal.ui.viewers.model.ITreeModelViewer; import org.eclipse.debug.internal.ui.viewers.model.provisional.ICheckUpdate; -import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate; -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.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.IModelProxy; import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta; -import org.eclipse.debug.internal.ui.viewers.provisional.AbstractModelProxy; import org.eclipse.jface.viewers.TreePath; -import org.eclipse.jface.viewers.Viewer; +import org.osgi.framework.BundleContext; /** * Test model for the use in unit tests. This test model contains a set of * elements in a tree structure. It contains utility methods for modifying the * model and for verifying that the viewer content matches the model. * - * @since 3.6 + * @since 2.2 */ -public class TestModel { +public class TestModel extends AbstractDsfService implements IFormattedValues { - public static class TestElement extends PlatformObject { + public interface TestElementValidator { + public void validate(TestElement modelElement, TestElement viewerElement, TreePath viewerPath); + } + + public static class TestElement extends AbstractDMContext implements IFormattedDataDMContext { private final TestModel fModel; private final String fID; TestElement[] fChildren; @@ -51,18 +58,37 @@ public class TestModel { boolean fChecked; boolean fGrayed; + private TestElement[] fParents = new TestElement[1]; + public TestElement(TestModel model, String text, TestElement[] children) { this (model, text, false, false, children); } public TestElement(TestModel model, String text, boolean checked, boolean grayed, TestElement[] children) { + super(model, EMPTY_PARENTS_ARRAY); fModel = model; fID = text; fChildren = children; + for (TestElement child : children) { + child.setParent(this); + } fChecked = checked; fGrayed = grayed; } + public void setParent(TestElement parent) { + fParents[0] = parent; + } + + public TestElement getParent() { + return fParents[0]; + } + + @Override + public IDMContext[] getParents() { + return fParents; + } + public TestModel getModel() { return fModel; } @@ -129,39 +155,72 @@ public class TestModel { } } - private class ModelProxy extends AbstractModelProxy { - @Override - public void installed(Viewer viewer) { - super.installed(viewer); - ModelDelta rootDelta = TestModel.this.getBaseDelta(new ModelDelta(fInput, IModelDelta.NO_CHANGE)); - installSubModelProxies(fRootPath, rootDelta); - fireModelChanged(rootDelta); - } - - private void installSubModelProxies(TreePath path, ModelDelta delta) { - TestElement element = getElement(path); - if (element.fModel != TestModel.this) { - // Found an element from a different model. Install its proxy and return. - delta.setFlags(delta.getFlags() | IModelDelta.INSTALL); - } else { - TestElement[] children = element.getChildren(); - - for (int i = 0; i < children.length; i++) { - installSubModelProxies(path.createChildPath(children[i]), delta.addNode(children[i], IModelDelta.NO_CHANGE)); - } - } + public static final class TestEvent { + private final TestElement fElement; + private final int fType; + + public TestEvent(TestElement element, int type) { + fElement = element; + fType = type; + } + + public TestElement getElement() { + return fElement; + } + + /** + * @see IModelDelta#getFlags() + */ + public int getType() { + return fType; } } - + + private static final IFormattedValuesListener NULL_LISTENER = new IFormattedValuesListener() { + public void formattedValueUpdated(FormattedValueDMContext formattedValueDmc) {} + }; + private TestElement fRoot; private Object fInput = null; private TreePath fRootPath = TreePath.EMPTY; - private ModelProxy fModelProxy; + private IFormattedValuesListener fListener = NULL_LISTENER; /** * Constructor private. Use static factory methods instead. */ - public TestModel() {} + public TestModel(DsfSession session) { + super(session); + } + + @Override + protected BundleContext getBundleContext() { + return DsfTestPlugin.getBundleContext(); + } + + @Override + public void initialize(RequestMonitor rm) { + super.initialize(new RequestMonitor(getExecutor(), rm) { + @Override + protected void handleSuccess() { + register(new String[0], new Hashtable<String, String>() ); + super.handleSuccess(); + } + }); + } + + @Override + public void shutdown(RequestMonitor rm) { + unregister(); + super.shutdown(rm); + } + + public void setTestModelListener(IFormattedValuesListener listener) { + if (listener != null) { + fListener = listener; + } else { + fListener = NULL_LISTENER; + } + } public TestElement getRootElement() { return fRoot; @@ -197,48 +256,6 @@ public class TestModel { return depth; } - public void update(IHasChildrenUpdate[] updates) { - for (int i = 0; i < updates.length; i++) { - TestElement element = (TestElement)updates[i].getElement(); - updates[i].setHasChilren(element.getChildren().length > 0); - updates[i].done(); - } - } - - public void update(IChildrenCountUpdate[] updates) { - for (int i = 0; i < updates.length; i++) { - TestElement element = (TestElement)updates[i].getElement(); - updates[i].setChildCount(element.getChildren().length); - updates[i].done(); - } - } - - public void update(IChildrenUpdate[] updates) { - for (int i = 0; i < updates.length; i++) { - TestElement element = (TestElement)updates[i].getElement(); - int endOffset = updates[i].getOffset() + updates[i].getLength(); - for (int j = updates[i].getOffset(); j < endOffset; j++) { - if (j < element.getChildren().length) { - updates[i].setChild(element.getChildren()[j], j); - } - } - updates[i].done(); - } - } - - public void update(ILabelUpdate[] updates) { - for (int i = 0; i < updates.length; i++) { - TestElement element = (TestElement)updates[i].getElement(); - updates[i].setLabel(element.fID, 0); - if (updates[i] instanceof ICheckUpdate && - Boolean.TRUE.equals(updates[i].getPresentationContext().getProperty(ICheckUpdate.PROP_CHECK))) - { - ((ICheckUpdate)updates[i]).setChecked(element.getChecked(), element.getGrayed()); - } - updates[i].done(); - } - } - public final static String ELEMENT_MEMENTO_ID = "id"; public void compareElements(IElementCompareRequest[] updates) { @@ -259,24 +276,12 @@ public class TestModel { } } - public void elementChecked(IPresentationContext context, Object viewerInput, TreePath path, boolean checked) { TestElement element = getElement(path); Assert.assertFalse(element.getGrayed()); element.setChecked(checked, false); } - public IModelProxy createTreeModelProxy(Object input, TreePath path, IPresentationContext context) { - fModelProxy = new ModelProxy(); - fInput = input; - fRootPath = path; - return fModelProxy; - } - - public IModelProxy getModelProxy() { - return fModelProxy; - } - public TestElement getElement(TreePath path) { if (path.getSegmentCount() == 0) { return getRootElement(); @@ -289,6 +294,14 @@ public class TestModel { return null; } } + + public TestElement getElementFromViewer(ITreeModelContentProviderTarget viewer, TreePath parentPath, int index) { + Object element = viewer.getChildElement(parentPath, index); + if (element instanceof TestElementVMContext) { + return ((TestElementVMContext)element).getElement(); + } + return null; + } public void setAllExpanded() { doSetExpanded(fRoot); @@ -314,10 +327,16 @@ public class TestModel { public void validateData(ITreeModelViewer viewer, TreePath path) { - validateData(viewer, path, false); + validateData(viewer, path, null, false); } - public void validateData(ITreeModelViewer _viewer, TreePath path, boolean expandedElementsOnly) { + public void validateData(ITreeModelViewer viewer, TreePath path, TestElementValidator validator) { + + validateData(viewer, path, validator, false); + } + + public void validateData(ITreeModelViewer _viewer, TreePath path, TestElementValidator validator, boolean expandedElementsOnly) { + ITreeModelContentProviderTarget viewer = (ITreeModelContentProviderTarget)_viewer; TestElement element = getElement(path); if ( Boolean.TRUE.equals(_viewer.getPresentationContext().getProperty(ICheckUpdate.PROP_CHECK)) ) { @@ -331,8 +350,17 @@ public class TestModel { Assert.assertEquals(children.length, viewer.getChildCount(path)); for (int i = 0; i < children.length; i++) { - Assert.assertEquals(children[i], viewer.getChildElement(path, i)); - validateData(viewer, path.createChildPath(children[i]), expandedElementsOnly); + Object viewerObject = viewer.getChildElement(path, i); + if (viewerObject instanceof TestElementVMContext) { + TreePath childPath = path.createChildPath(viewerObject); + TestElement viewerElement = ((TestElementVMContext)viewerObject).getElement(); + Assert.assertEquals(children[i], viewerElement); + if (validator != null) { + validator.validate(children[i], viewerElement, childPath); + } + + validateData(viewer, childPath, validator, expandedElementsOnly); + } } } else if (!viewer.getExpandedState(path)) { // If element not expanded, verify the plus sign. @@ -344,10 +372,6 @@ public class TestModel { fRoot = root; } - public void postDelta(IModelDelta delta) { - fModelProxy.fireModelChanged(delta); - } - /** Create or retrieve delta for given path * @param combine if then new deltas for the given path are created. If false existing ones are reused. */ @@ -560,6 +584,27 @@ public class TestModel { return null; } + public void getAvailableFormats(IFormattedDataDMContext dmc, DataRequestMonitor<String[]> rm) { + rm.setData(new String[] { HEX_FORMAT, DECIMAL_FORMAT, OCTAL_FORMAT, BINARY_FORMAT, NATURAL_FORMAT }); + rm.done(); + } + + public void getFormattedExpressionValue(FormattedValueDMContext dmc, DataRequestMonitor<FormattedValueDMData> rm) { + TestElement te = DMContexts.getAncestorOfType(dmc, TestElement.class); + rm.setData(new FormattedValueDMData( getFormattedValueText(te, dmc.getFormatID()))); + rm.done(); + fListener.formattedValueUpdated(dmc); + } + + public FormattedValueDMContext getFormattedValueContext(IFormattedDataDMContext dmc, String formatId) { + // Creates a context that can be used to retrieve a formatted value. + return new FormattedValueDMContext(this, dmc, formatId); + } + + public String getFormattedValueText(TestElement te, String formatId) { + return te.getLabel() + " (" + formatId + ")"; + } + @Override public String toString() { return getElementString(fRoot, ""); @@ -577,8 +622,7 @@ public class TestModel { return builder.toString(); } - public static TestModel simpleSingleLevel() { - TestModel model = new TestModel(); + public static void simpleSingleLevel(TestModel model) { model.setRoot( new TestElement(model, "root", new TestElement[] { new TestElement(model, "1", true, true, new TestElement[0]), new TestElement(model, "2", true, false, new TestElement[0]), @@ -587,11 +631,9 @@ public class TestModel { new TestElement(model, "5", new TestElement[0]), new TestElement(model, "6", new TestElement[0]) }) ); - return model; } - public static TestModel simpleMultiLevel() { - TestModel model = new TestModel(); + public static void simpleMultiLevel(TestModel model) { model.setRoot( new TestElement(model, "root", new TestElement[] { new TestElement(model, "1", new TestElement[0]), new TestElement(model, "2", true, false, new TestElement[] { @@ -617,53 +659,6 @@ public class TestModel { }), }) }) ); - return model; } - - public static TestModel compositeMultiLevel() { - TestModel m2 = new TestModel(); - m2.setRoot( new TestElement(m2, "m2.root", new TestElement[] { - new TestElement(m2, "m2.1", new TestElement[0]), - new TestElement(m2, "m2.2", true, false, new TestElement[] { - new TestElement(m2, "m2.2.1", true, true, new TestElement[0]), - new TestElement(m2, "m2.2.2", false, true, new TestElement[0]), - new TestElement(m2, "m2.2.3", true, false, new TestElement[0]), - }), - }) ); - - TestModel m3 = new TestModel(); - m3.setRoot( new TestElement(m3, "m3.root", new TestElement[] { - new TestElement(m3, "m3.1", new TestElement[0]), - new TestElement(m3, "m3.2", true, false, new TestElement[] { - new TestElement(m3, "m3.2.1", true, true, new TestElement[0]), - new TestElement(m3, "m3.2.2", false, true, new TestElement[0]), - new TestElement(m3, "m3.2.3", true, false, new TestElement[0]), - }), - }) ); - - TestModel m4 = new TestModel(); - m4.setRoot( new TestElement(m4, "m4.root", new TestElement[] { - new TestElement(m4, "m4.1", new TestElement[0]), - new TestElement(m4, "m4.2", true, false, new TestElement[] { - new TestElement(m4, "m4.2.1", true, true, new TestElement[0]), - new TestElement(m4, "m4.2.2", false, true, new TestElement[0]), - new TestElement(m4, "m4.2.3", true, false, new TestElement[0]), - }), - }) ); - - TestModel m1 = new TestModel(); - m1.setRoot( new TestElement(m1, "m1.root", new TestElement[] { - new TestElement(m1, "m1.1", new TestElement[0]), - new TestElement(m1, "m1.2", true, false, new TestElement[] { - m2.fRoot, - m3.fRoot, - m4.fRoot, - }), - }) ); - - - return m1; - } - } diff --git a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/TestModelCachingVMProvider.java b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/TestModelCachingVMProvider.java new file mode 100644 index 00000000000..5279e4afb79 --- /dev/null +++ b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/TestModelCachingVMProvider.java @@ -0,0 +1,113 @@ +/******************************************************************************* + * Copyright (c) 2010 Wind River 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.tests.dsf.vm; + +import org.eclipse.cdt.dsf.debug.ui.viewmodel.update.BreakpointHitUpdatePolicy; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.update.DebugManualUpdatePolicy; +import org.eclipse.cdt.dsf.service.DsfSession; +import org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMAdapter; +import org.eclipse.cdt.dsf.ui.viewmodel.IVMNode; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMProvider; +import org.eclipse.cdt.dsf.ui.viewmodel.update.AutomaticUpdatePolicy; +import org.eclipse.cdt.dsf.ui.viewmodel.update.IVMUpdatePolicy; +import org.eclipse.cdt.tests.dsf.vm.TestModel.TestElement; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentation; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.jface.util.PropertyChangeEvent; + +/** + * @since 2.2 + */ +public class TestModelCachingVMProvider extends AbstractDMVMProvider { + + public static final String COLUMN_ID = "COLUMN_ID"; + public static final String COLUMN_FORMATTED_VALUE = "COLUMN_FORMATTED_VALUE"; + public static final String COLUMN_DUMMY_VALUE = "COLUMN_DUMMY_VALUE"; + private static final String[] COLUMNS = new String[] { COLUMN_ID, COLUMN_FORMATTED_VALUE, COLUMN_DUMMY_VALUE }; + + private IPropertyChangeListener fPresentationContextListener = new IPropertyChangeListener() { + public void propertyChange(PropertyChangeEvent event) { + handleEvent(event); + } + }; + + private static IColumnPresentation COLUMN_PRESENTATION = new IColumnPresentation() { + public void init(IPresentationContext context) {} + public void dispose() {}; + + public String[] getAvailableColumns() { + return COLUMNS; + }; + + public String getHeader(String id) { + return id; + }; + + public String getId() { return "ID"; } + + public ImageDescriptor getImageDescriptor(String id) { + return null; + } + public String[] getInitialColumns() { + return COLUMNS; + } + public boolean isOptional() { + return false; + } + }; + + public TestModelCachingVMProvider(AbstractVMAdapter adapter, IPresentationContext context, DsfSession session) { + super(adapter, context, session); + + setRootNode(new TestModelDMVMNode(this, session)); + addChildNodes(getRootVMNode(), new IVMNode[] { getRootVMNode() }); + + context.addPropertyChangeListener(fPresentationContextListener); + } + + @Override + protected IVMUpdatePolicy[] createUpdateModes() { + return new IVMUpdatePolicy[] { + new AutomaticUpdatePolicy(), + new DebugManualUpdatePolicy(new String[] { TestModelDMVMNode.PROP_PREFIX_DUMMY }), + new BreakpointHitUpdatePolicy() }; + } + + public TestModelDMVMNode getNode() { + return (TestModelDMVMNode)getRootVMNode(); + } + + @Override + public void dispose() { + getPresentationContext().removePropertyChangeListener(fPresentationContextListener); + super.dispose(); + } + + public void postEvent(Object event) { + super.handleEvent(event); + } + + public TestElementVMContext getElementVMContext(IPresentationContext context, TestElement element) { + return ((TestModelDMVMNode)getRootVMNode()).createVMContext(element); + } + + @Override + public String getColumnPresentationId(IPresentationContext context, Object element) { + return COLUMN_PRESENTATION.getId(); + } + + @Override + public IColumnPresentation createColumnPresentation(IPresentationContext context, Object element) { + return COLUMN_PRESENTATION; + } +} diff --git a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/TestModelDMVMNode.java b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/TestModelDMVMNode.java new file mode 100644 index 00000000000..19f15555ec8 --- /dev/null +++ b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/TestModelDMVMNode.java @@ -0,0 +1,252 @@ +/******************************************************************************* + * Copyright (c) 2008, 2010 Wind River 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: + * Wind River Systems - initial API and implementation + *******************************************************************************/ +package org.eclipse.cdt.tests.dsf.vm; + +import org.eclipse.cdt.dsf.concurrent.CountingRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; +import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.IDebugVMConstants; +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.service.DsfSession; +import org.eclipse.cdt.dsf.ui.viewmodel.IRootVMNode; +import org.eclipse.cdt.dsf.ui.viewmodel.VMDelta; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMNode; +import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMProvider; +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.LabelColumnInfo; +import org.eclipse.cdt.dsf.ui.viewmodel.properties.LabelText; +import org.eclipse.cdt.dsf.ui.viewmodel.properties.PropertiesBasedLabelProvider; +import org.eclipse.cdt.tests.dsf.ViewerUpdatesListener; +import org.eclipse.cdt.tests.dsf.vm.TestModel.TestElement; +import org.eclipse.cdt.tests.dsf.vm.TestModel.TestEvent; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; +import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementLabelProvider; +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.jface.util.PropertyChangeEvent; + +/** + * @since 2.2 + */ +public class TestModelDMVMNode extends AbstractDMVMNode implements IRootVMNode, IElementLabelProvider, IElementPropertiesProvider { + + final public static String PROP_PREFIX_DUMMY = "dummy."; + + final private static String PROP_TEST_ELEMENT_LABEL = "PROP_TEST_ELEMENT_LABEL"; + + private ViewerUpdatesListener fViewerUpdateListener = NULL_VIEWER_UPDATE_LISTENER; + private FormattedValuesListener fFormattedValuesListener; + + private static final ViewerUpdatesListener NULL_VIEWER_UPDATE_LISTENER = new ViewerUpdatesListener(); + + final private static PropertiesBasedLabelProvider fLabelProvider = new PropertiesBasedLabelProvider(); + { + LabelColumnInfo idLabelInfo = new LabelColumnInfo(new LabelAttribute[] { + new LabelText("{0}", new String[] { PROP_TEST_ELEMENT_LABEL }) + }); + + fLabelProvider.setColumnInfo(PropertiesBasedLabelProvider.ID_COLUMN_NO_COLUMNS, idLabelInfo); + fLabelProvider.setColumnInfo(TestModelCachingVMProvider.COLUMN_ID, idLabelInfo); + fLabelProvider.setColumnInfo( + TestModelCachingVMProvider.COLUMN_FORMATTED_VALUE, + new LabelColumnInfo(new LabelAttribute[] { + new FormattedValueLabelText() + })); + fLabelProvider.setColumnInfo( + TestModelCachingVMProvider.COLUMN_DUMMY_VALUE, + new LabelColumnInfo(new LabelAttribute[] { + new FormattedValueLabelText(PROP_PREFIX_DUMMY) + })); + } + + private final FormattedValueRetriever fFormattedValueRetriever; + private final FormattedValueRetriever fDummyFormattedValueRetriever; + + public TestModelDMVMNode(AbstractDMVMProvider provider, DsfSession session) { + super(provider, session, TestElement.class); + fFormattedValueRetriever = new FormattedValueRetriever(this, getSession(), TestModel.class, TestElement.class); + fDummyFormattedValueRetriever = new FormattedValueRetriever(this, getSession(), DummyFormattedValueService.class, TestElement.class, PROP_PREFIX_DUMMY); + } + + @Override + public void dispose() { + super.dispose(); + fFormattedValueRetriever.dispose(); + fDummyFormattedValueRetriever.dispose(); + } + + public void setVMUpdateListener(ViewerUpdatesListener viewerUpdateListener) { + if (viewerUpdateListener != null) { + fViewerUpdateListener = viewerUpdateListener; + } else { + fViewerUpdateListener = NULL_VIEWER_UPDATE_LISTENER; + } + } + + public void setFormattedValuesListener(FormattedValuesListener formattedValuesListener) { + fFormattedValuesListener = formattedValuesListener; + } + + public PropertiesBasedLabelProvider getLabelProvider() { + return fLabelProvider; + } + + public void update(final ILabelUpdate[] updates) { + fLabelProvider.update(updates); + } + + @Override + public void update(IHasChildrenUpdate[] updates) { + fViewerUpdateListener.viewerUpdatesBegin(); + for (IHasChildrenUpdate update : updates) { + fViewerUpdateListener.updateStarted(update); + if (update.getElement() instanceof TestElementVMContext) { + TestElement element = ((TestElementVMContext)update.getElement()).getElement(); + update.setHasChilren(element.getChildren().length != 0); + } + update.done(); + fViewerUpdateListener.updateComplete(update); + } + fViewerUpdateListener.viewerUpdatesComplete(); + } + + @Override + public void update(IChildrenCountUpdate[] updates) { + fViewerUpdateListener.viewerUpdatesBegin(); + for (IChildrenCountUpdate update : updates) { + fViewerUpdateListener.updateStarted(update); + if (update.getElement() instanceof TestElementVMContext) { + TestElement element = ((TestElementVMContext)update.getElement()).getElement(); + update.setChildCount(element.getChildren().length); + } + update.done(); + fViewerUpdateListener.updateComplete(update); + } + fViewerUpdateListener.viewerUpdatesComplete(); + } + + @Override + public void update(IChildrenUpdate[] updates) { + fViewerUpdateListener.viewerUpdatesBegin(); + for (IChildrenUpdate update : updates) { + fViewerUpdateListener.updateStarted(update); + if (update.getElement() instanceof TestElementVMContext) { + TestElement element = ((TestElementVMContext)update.getElement()).getElement(); + fillUpdateWithTestElements(update, element.getChildren()); + } + update.done(); + fViewerUpdateListener.updateComplete(update); + } + fViewerUpdateListener.viewerUpdatesComplete(); + } + + + @Override + protected void updateElementsInSessionThread(IChildrenUpdate update) { + // TODO Auto-generated method stub + + } + + public void update(final IPropertiesUpdate[] updates) { + fViewerUpdateListener.propertiesUpdatesStarted(updates); + if (fFormattedValuesListener != null) fFormattedValuesListener.propertiesUpdatesStarted(updates); + + CountingRequestMonitor crm = new CountingRequestMonitor(getExecutor(), null) { + @Override + protected void handleSuccess() { + for (IPropertiesUpdate update : updates) { + if (update.getElement() instanceof TestElementVMContext) { + TestElement element = ((TestElementVMContext)update.getElement()).getElement(); + update.setProperty(PROP_TEST_ELEMENT_LABEL, element.getLabel()); + } + update.done(); + fViewerUpdateListener.propertiesUpdateCompleted(update); + if (fFormattedValuesListener != null) fFormattedValuesListener.propertiesUpdateCompleted(update); + } + } + }; + int count = 0; + + fFormattedValueRetriever.update(updates, crm); + count++; + fDummyFormattedValueRetriever.update(updates, crm); + count++; + crm.setDoneCount(count); + } + + private void fillUpdateWithTestElements(IChildrenUpdate update, TestElement[] modelElements) { + int updateIdx = update.getOffset() != -1 ? update.getOffset() : 0; + int endIdx = updateIdx + (update.getLength() != -1 ? update.getLength() : modelElements.length); + while (updateIdx < endIdx && updateIdx < modelElements.length) { + update.setChild(createVMContext(modelElements[updateIdx]), updateIdx); + updateIdx++; + } + } + + public TestElementVMContext createVMContext(TestElement element) { + return new TestElementVMContext(this, element); + } + + /* + * (non-Javadoc) + * @see org.eclipse.cdt.dsf.ui.viewmodel.IVMNode#getDeltaFlags(java.lang.Object) + */ + public int getDeltaFlags(Object e) { + if ( e instanceof PropertyChangeEvent && + ((PropertyChangeEvent)e).getProperty() == IDebugVMConstants.PROP_FORMATTED_VALUE_FORMAT_PREFERENCE) + { + return IModelDelta.CONTENT; + } + if (e instanceof TestEvent) { + return ((TestEvent)e).getType(); + } + + return IModelDelta.NO_CHANGE; + } + + public void buildDelta(Object e, VMDelta parent, int nodeOffset, RequestMonitor rm) { + if ( e instanceof PropertyChangeEvent && + ((PropertyChangeEvent)e).getProperty() == IDebugVMConstants.PROP_FORMATTED_VALUE_FORMAT_PREFERENCE) + { + parent.setFlags(parent.getFlags() | IModelDelta.CONTENT); + } + rm.done(); + } + + + public boolean isDeltaEvent(Object rootObject, Object event) { + return getDeltaFlags(event) != IModelDelta.NO_CHANGE; + } + + public void createRootDelta(Object rootObject, Object event, DataRequestMonitor<VMDelta> rm) { + int flags = IModelDelta.NO_CHANGE; + if ( event instanceof PropertyChangeEvent && + ((PropertyChangeEvent)event).getProperty() == IDebugVMConstants.PROP_FORMATTED_VALUE_FORMAT_PREFERENCE) + { + flags |= IModelDelta.CONTENT; + } + + // TODO: make more sophisticated to update specific elements. + if (event instanceof TestEvent) { + flags|= ((TestEvent)event).getType(); + } + + rm.setData( new VMDelta(rootObject, 0, flags) ); + rm.done(); + } + + +} diff --git a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/TestModelUpdatesListener.java b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/TestModelUpdatesListener.java index 8684a81d038..a98d0a4f97f 100644 --- a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/TestModelUpdatesListener.java +++ b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/TestModelUpdatesListener.java @@ -11,34 +11,21 @@ package org.eclipse.cdt.tests.dsf.vm; import java.util.Comparator; -import java.util.HashSet; -import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; -import junit.framework.Assert; - +import org.eclipse.cdt.tests.dsf.ViewerUpdatesListener; import org.eclipse.cdt.tests.dsf.vm.TestModel.TestElement; -import org.eclipse.debug.internal.ui.viewers.model.ILabelUpdateListener; import org.eclipse.debug.internal.ui.viewers.model.ITreeModelContentProviderTarget; import org.eclipse.debug.internal.ui.viewers.model.ITreeModelViewer; -import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate; -import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate; -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.IModelChangedListener; -import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; -import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelProxy; -import org.eclipse.debug.internal.ui.viewers.model.provisional.IStateUpdateListener; -import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate; -import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdateListener; import org.eclipse.jface.viewers.TreePath; -public class TestModelUpdatesListener - implements IViewerUpdateListener, ILabelUpdateListener, IModelChangedListener, ITestModelUpdatesListenerConstants, - IStateUpdateListener +/** + * @since 2.2 + */ +public class TestModelUpdatesListener extends ViewerUpdatesListener { private final static Comparator<String> fStringComparator = new Comparator<String>() { @@ -116,74 +103,28 @@ public class TestModelUpdatesListener }; - private final ITreeModelViewer fViewer; - - private boolean fFailOnRedundantUpdates; - private boolean fFailOnMultipleModelUpdateSequences; - private boolean fFailOnMultipleLabelUpdateSequences; - - private Set<TreePath> fHasChildrenUpdates = new TreeSet<TreePath>(fTestElementVMCComparator); - private Map<TreePath, Set<Integer>> fChildrenUpdates = new TreeMap<TreePath, Set<Integer>>(fTestElementVMCComparator); - private Set<TreePath> fChildCountUpdates = new TreeSet<TreePath>(fTestElementVMCComparator); - private Set<TreePath> fLabelUpdates = new TreeSet<TreePath>(fTestElementVMCComparator); - private Set<TestModel> fProxyModels = new HashSet<TestModel>(); - private Set<TreePath> fStateUpdates = new TreeSet<TreePath>(fTestElementVMCComparator); - private boolean fViewerUpdatesComplete; - private boolean fLabelUpdatesComplete; - private boolean fModelChangedComplete; - private boolean fStateSaveComplete; - private boolean fStateRestoreComplete; - private int fViewerUpdatesRunning; - private int fLabelUpdatesRunning; - private int fTimeoutInterval = 60000; - private long fTimeoutTime; + @Override + protected Set<TreePath> makeTreePathSet() { + return new TreeSet<TreePath>(fTestElementVMCComparator); + } - - public TestModelUpdatesListener(ITreeModelViewer viewer, boolean failOnRedundantUpdates, boolean failOnMultipleModelUpdateSequences) { - setFailOnRedundantUpdates(failOnRedundantUpdates); - setFailOnMultipleModelUpdateSequences(failOnMultipleModelUpdateSequences); - fViewer = viewer; - fViewer.addLabelUpdateListener(this); - fViewer.addModelChangedListener(this); - fViewer.addStateUpdateListener(this); - fViewer.addViewerUpdateListener(this); - } - - public void dispose() { - fViewer.removeLabelUpdateListener(this); - fViewer.removeModelChangedListener(this); - fViewer.removeStateUpdateListener(this); - fViewer.removeViewerUpdateListener(this); - } - - - public void setFailOnRedundantUpdates(boolean failOnRedundantUpdates) { - fFailOnRedundantUpdates = failOnRedundantUpdates; - } - - public void setFailOnMultipleModelUpdateSequences(boolean failOnMultipleLabelUpdateSequences) { - fFailOnMultipleModelUpdateSequences = failOnMultipleLabelUpdateSequences; + @Override + protected <V> Map<TreePath, V> makeTreePathMap() { + return new TreeMap<TreePath, V>(fTestElementVMCComparator); } + - public void setFailOnMultipleLabelUpdateSequences(boolean failOnMultipleLabelUpdateSequences) { - fFailOnMultipleLabelUpdateSequences = failOnMultipleLabelUpdateSequences; + public TestModelUpdatesListener() { + super(); } - /** - * Sets the the maximum amount of time (in milliseconds) that the update listener - * is going to wait. If set to -1, the listener will wait indefinitely. - */ - public void setTimeoutInterval(int milis) { - fTimeoutInterval = milis; + public TestModelUpdatesListener(ITreeModelViewer viewer, boolean failOnRedundantUpdates, boolean failOnMultipleModelUpdateSequences) { + super(viewer, failOnRedundantUpdates, failOnMultipleModelUpdateSequences); } public void reset(TreePath path, TestElement element, int levels, boolean failOnRedundantUpdates, boolean failOnMultipleUpdateSequences) { - reset(); + reset(failOnRedundantUpdates, failOnMultipleUpdateSequences); addUpdates(path, element, levels); - addProxies(element); - setFailOnRedundantUpdates(failOnRedundantUpdates); - setFailOnMultipleModelUpdateSequences(failOnMultipleUpdateSequences); - setFailOnMultipleLabelUpdateSequences(false); } public void reset(boolean failOnRedundantUpdates, boolean failOnMultipleUpdateSequences) { @@ -193,67 +134,6 @@ public class TestModelUpdatesListener setFailOnMultipleLabelUpdateSequences(false); } - public void reset() { - fHasChildrenUpdates.clear(); - fChildrenUpdates.clear(); - fChildCountUpdates.clear(); - fLabelUpdates.clear(); - fProxyModels.clear(); - fViewerUpdatesComplete = false; - fLabelUpdatesComplete = false; - fStateSaveComplete = false; - fStateRestoreComplete = false; - fTimeoutTime = System.currentTimeMillis() + fTimeoutInterval; - resetModelChanged(); - } - - public void resetModelChanged() { - fModelChangedComplete = false; - } - - public void addHasChildrenUpdate(TreePath path) { - fHasChildrenUpdates.add(path); - } - - public void removeHasChildrenUpdate(TreePath path) { - fHasChildrenUpdates.remove(path); - } - - public void addChildreCountUpdate(TreePath path) { - fChildCountUpdates.add(path); - } - - public void removeChildreCountUpdate(TreePath path) { - fChildCountUpdates.remove(path); - } - - public void addChildreUpdate(TreePath path, int index) { - Set<Integer> childrenIndexes = fChildrenUpdates.get(path); - if (childrenIndexes == null) { - childrenIndexes = new TreeSet<Integer>(); - fChildrenUpdates.put(path, childrenIndexes); - } - childrenIndexes.add(new Integer(index)); - } - - public void removeChildrenUpdate(TreePath path, int index) { - Set<Integer> childrenIndexes = fChildrenUpdates.get(path); - if (childrenIndexes != null) { - childrenIndexes.remove(new Integer(index)); - if (childrenIndexes.isEmpty()) { - fChildrenUpdates.remove(path); - } - } - } - - public void addLabelUpdate(TreePath path) { - fLabelUpdates.add(path); - } - - public void removeLabelUpdate(TreePath path) { - fLabelUpdates.remove(path); - } - public void addUpdates(TreePath path, TestElement element, int levels) { addUpdates(path, element, levels, ALL_UPDATES_COMPLETE); } @@ -269,29 +149,30 @@ public class TestModelUpdatesListener public void addUpdates(ITreeModelContentProviderTarget viewer, TreePath path, TestElement element, int levels, int flags) { if (!path.equals(TreePath.EMPTY)) { if ((flags & LABEL_UPDATES) != 0) { - fLabelUpdates.add(path); + addLabelUpdate(path); + } + if ((flags & PROPERTY_UPDATES) != 0) { + addPropertiesUpdate(path); } if ((flags & HAS_CHILDREN_UPDATES) != 0) { - fHasChildrenUpdates.add(path); + addHasChildrenUpdate(path); } } if (levels-- != 0) { TestElement[] children = element.getChildren(); if (children.length > 0 && (viewer == null || path.getSegmentCount() == 0 || viewer.getExpandedState(path))) { - if ((flags & CHILDREN_COUNT_UPDATES) != 0) { - fChildCountUpdates.add(path); + if ((flags & CHILD_COUNT_UPDATES) != 0) { + addChildCountUpdate(path); } if ((flags & CHILDREN_UPDATES) != 0) { - Set<Integer> childrenIndexes = new HashSet<Integer>(); for (int i = 0; i < children.length; i++) { - childrenIndexes.add(new Integer(i)); + addChildreUpdate(path, i); } - fChildrenUpdates.put(path, childrenIndexes); } if ((flags & STATE_UPDATES) != 0 && viewer != null) { - fStateUpdates.add(path); + addStateUpdate(path); } for (int i = 0; i < children.length; i++) { @@ -301,284 +182,6 @@ public class TestModelUpdatesListener } } - - private void addProxies(TestElement element) { - TestModel model = element.getModel(); - if (model.getModelProxy() == null) { - fProxyModels.add(element.getModel()); - } - TestElement[] children = element.getChildren(); - for (int i = 0; i < children.length; i++) { - addProxies(children[i]); - } - } - - public boolean isFinished() { - return isFinished(ALL_UPDATES_COMPLETE); - } - - public boolean isFinished(int flags) { - if (fTimeoutInterval > 0 && fTimeoutTime < System.currentTimeMillis()) { - throw new RuntimeException("Timed Out: " + toString(flags)); - } - - if ( (flags & LABEL_UPDATES_COMPLETE) != 0) { - if (!fLabelUpdatesComplete) return false; - } - if ( (flags & LABEL_UPDATES) != 0) { - if (!fLabelUpdates.isEmpty()) return false; - } - if ( (flags & CONTENT_UPDATES_COMPLETE) != 0) { - if (!fViewerUpdatesComplete) return false; - } - if ( (flags & HAS_CHILDREN_UPDATES) != 0) { - if (!fHasChildrenUpdates.isEmpty()) return false; - } - if ( (flags & CHILDREN_COUNT_UPDATES) != 0) { - if (!fChildCountUpdates.isEmpty()) return false; - } - if ( (flags & CHILDREN_UPDATES) != 0) { - if (!fChildrenUpdates.isEmpty()) return false; - } - if ( (flags & MODEL_CHANGED_COMPLETE) != 0) { - if (!fModelChangedComplete) return false; - } - if ( (flags & STATE_SAVE_COMPLETE) != 0) { - if (!fStateSaveComplete) return false; - } - if ( (flags & STATE_RESTORE_COMPLETE) != 0) { - if (!fStateRestoreComplete) return false; - } - if ( (flags & MODEL_PROXIES_INSTALLED) != 0) { - if (fProxyModels.size() != 0) return false; - } - if ( (flags & VIEWER_UPDATES_RUNNING) != 0) { - if (fViewerUpdatesRunning != 0) { - return false; - } - } - if ( (flags & LABEL_UPDATES_RUNNING) != 0) { - if (fLabelUpdatesRunning != 0) { - return false; - } - } - - return true; - } - - public void updateStarted(IViewerUpdate update) { - synchronized (this) { - fViewerUpdatesRunning++; - } - } - - public void updateComplete(IViewerUpdate update) { - synchronized (this) { - fViewerUpdatesRunning--; - } - - if (!update.isCanceled()) { - if (update instanceof IHasChildrenUpdate) { - if (!fHasChildrenUpdates.remove(update.getElementPath()) && fFailOnRedundantUpdates) { - Assert.fail("Redundant update: " + update); - } - } if (update instanceof IChildrenCountUpdate) { - if (!fChildCountUpdates.remove(update.getElementPath()) && fFailOnRedundantUpdates) { - Assert.fail("Redundant update: " + update); - } - } else if (update instanceof IChildrenUpdate) { - int start = ((IChildrenUpdate)update).getOffset(); - int end = start + ((IChildrenUpdate)update).getLength(); - - Set<Integer> childrenIndexes = fChildrenUpdates.get(update.getElementPath()); - if (childrenIndexes != null) { - for (int i = start; i < end; i++) { - childrenIndexes.remove(new Integer(i)); - } - if (childrenIndexes.isEmpty()) { - fChildrenUpdates.remove(update.getElementPath()); - } - } else if (fFailOnRedundantUpdates) { - Assert.fail("Redundant update: " + update); - } - } - } - } - - public void viewerUpdatesBegin() { - - } - - public void viewerUpdatesComplete() { - if (fFailOnMultipleModelUpdateSequences && fViewerUpdatesComplete) { - Assert.fail("Multiple viewer update sequences detected"); - } - fViewerUpdatesComplete = true; - } - - public void labelUpdateComplete(ILabelUpdate update) { - synchronized (this) { - fLabelUpdatesRunning--; - } - if (!fLabelUpdates.remove(update.getElementPath()) && fFailOnRedundantUpdates) { - Assert.fail("Redundant update: " + update); - } - } - - public void labelUpdateStarted(ILabelUpdate update) { - synchronized (this) { - fLabelUpdatesRunning++; - } - } - - public void labelUpdatesBegin() { - } - - public void labelUpdatesComplete() { - if (fFailOnMultipleLabelUpdateSequences && fLabelUpdatesComplete) { - Assert.fail("Multiple label update sequences detected"); - } - fLabelUpdatesComplete = true; - } - - public void modelChanged(IModelDelta delta, IModelProxy proxy) { - fModelChangedComplete = true; - - for (Iterator<TestModel> itr = fProxyModels.iterator(); itr.hasNext();) { - TestModel model = itr.next(); - if (model.getModelProxy() == proxy) { - itr.remove(); - break; - } - } - } - - public void stateRestoreUpdatesBegin(Object input) { - } - - public void stateRestoreUpdatesComplete(Object input) { - fStateRestoreComplete = true; - } - - public void stateSaveUpdatesBegin(Object input) { - } - - public void stateSaveUpdatesComplete(Object input) { - fStateSaveComplete = true; - } - - public void stateUpdateComplete(Object input, IViewerUpdate update) { - } - - public void stateUpdateStarted(Object input, IViewerUpdate update) { - } - - private String toString(int flags) { - StringBuffer buf = new StringBuffer("Viewer Update Listener"); - - if ( (flags & LABEL_UPDATES_COMPLETE) != 0) { - buf.append("\n\t"); - buf.append("fLabelUpdatesComplete = " + fLabelUpdatesComplete); - } - if ( (flags & LABEL_UPDATES_RUNNING) != 0) { - buf.append("\n\t"); - buf.append("fLabelUpdatesRunning = " + fLabelUpdatesRunning); - } - if ( (flags & LABEL_UPDATES) != 0) { - buf.append("\n\t"); - buf.append("fLabelUpdates = "); - buf.append( toString(fLabelUpdates) ); - } - if ( (flags & CONTENT_UPDATES_COMPLETE) != 0) { - buf.append("\n\t"); - buf.append("fViewerUpdatesComplete = " + fViewerUpdatesComplete); - } - if ( (flags & VIEWER_UPDATES_RUNNING) != 0) { - buf.append("\n\t"); - buf.append("fViewerUpdatesRunning = " + fViewerUpdatesRunning); - } - if ( (flags & HAS_CHILDREN_UPDATES) != 0) { - buf.append("\n\t"); - buf.append("fHasChildrenUpdates = "); - buf.append( toString(fHasChildrenUpdates) ); - } - if ( (flags & CHILDREN_COUNT_UPDATES) != 0) { - buf.append("\n\t"); - buf.append("fChildCountUpdates = "); - buf.append( toString(fChildCountUpdates) ); - } - if ( (flags & CHILDREN_UPDATES) != 0) { - buf.append("\n\t"); - buf.append("fChildrenUpdates = "); - buf.append( toString(fChildrenUpdates) ); - } - if ( (flags & MODEL_CHANGED_COMPLETE) != 0) { - buf.append("\n\t"); - buf.append("fModelChangedComplete = " + fModelChangedComplete); - } - if ( (flags & STATE_SAVE_COMPLETE) != 0) { - buf.append("\n\t"); - buf.append("fStateSaveComplete = " + fStateSaveComplete); - } - if ( (flags & STATE_RESTORE_COMPLETE) != 0) { - buf.append("\n\t"); - buf.append("fStateRestoreComplete = " + fStateRestoreComplete); - } - if ( (flags & MODEL_PROXIES_INSTALLED) != 0) { - buf.append("\n\t"); - buf.append("fProxyModels = " + fProxyModels); - } - if (fTimeoutInterval > 0) { - buf.append("\n\t"); - buf.append("fTimeoutInterval = " + fTimeoutInterval); - } - return buf.toString(); - } - - private String toString(Set<TreePath> set) { - if (set.isEmpty()) { - return "(EMPTY)"; - } - StringBuffer buf = new StringBuffer(); - for (Iterator<TreePath> itr = set.iterator(); itr.hasNext(); ) { - buf.append("\n\t\t"); - buf.append(toString(itr.next())); - } - return buf.toString(); - } - - private String toString(Map<TreePath, Set<Integer>> map) { - if (map.isEmpty()) { - return "(EMPTY)"; - } - StringBuffer buf = new StringBuffer(); - for (Iterator<TreePath> itr = map.keySet().iterator(); itr.hasNext(); ) { - buf.append("\n\t\t"); - TreePath path = itr.next(); - buf.append(toString(path)); - Set<?> updates = map.get(path); - buf.append(" = "); - buf.append(updates.toString()); - } - return buf.toString(); - } - - private String toString(TreePath path) { - if (path.getSegmentCount() == 0) { - return "/"; - } - StringBuffer buf = new StringBuffer(); - for (int i = 0; i < path.getSegmentCount(); i++) { - buf.append("/"); - buf.append(path.getSegment(i)); - } - return buf.toString(); - } - - @Override - public String toString() { - return toString(ALL_UPDATES_COMPLETE | MODEL_CHANGED_COMPLETE | STATE_RESTORE_COMPLETE); - } } diff --git a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/TestModelVMAdapter.java b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/TestModelVMAdapter.java index 2e610714c79..90d37eb6dfb 100644 --- a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/TestModelVMAdapter.java +++ b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/TestModelVMAdapter.java @@ -15,7 +15,7 @@ import org.eclipse.cdt.dsf.ui.viewmodel.IVMProvider; import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; /** - * + * @since 2.2 */ public class TestModelVMAdapter extends AbstractVMAdapter { diff --git a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/TestModelVMNode.java b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/TestModelVMNode.java index 959dd2d5b4b..c7f11ac9988 100644 --- a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/TestModelVMNode.java +++ b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/TestModelVMNode.java @@ -13,9 +13,10 @@ package org.eclipse.cdt.tests.dsf.vm; import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants; import org.eclipse.cdt.dsf.concurrent.RequestMonitor; +import org.eclipse.cdt.dsf.debug.ui.viewmodel.numberformat.FormattedValueLabelText; import org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMNode; +import org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMProvider; import org.eclipse.cdt.dsf.ui.viewmodel.IRootVMNode; -import org.eclipse.cdt.dsf.ui.viewmodel.IVMProvider; import org.eclipse.cdt.dsf.ui.viewmodel.VMDelta; import org.eclipse.cdt.dsf.ui.viewmodel.properties.IElementPropertiesProvider; import org.eclipse.cdt.dsf.ui.viewmodel.properties.IPropertiesUpdate; @@ -23,6 +24,7 @@ import org.eclipse.cdt.dsf.ui.viewmodel.properties.LabelAttribute; import org.eclipse.cdt.dsf.ui.viewmodel.properties.LabelColumnInfo; import org.eclipse.cdt.dsf.ui.viewmodel.properties.LabelText; import org.eclipse.cdt.dsf.ui.viewmodel.properties.PropertiesBasedLabelProvider; +import org.eclipse.cdt.tests.dsf.DsfTestPlugin; import org.eclipse.cdt.tests.dsf.vm.TestModel.TestElement; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; @@ -33,7 +35,7 @@ import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdat import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate; /** - * + * @since 2.2 */ public class TestModelVMNode extends AbstractVMNode implements IRootVMNode, IElementLabelProvider, IElementPropertiesProvider { @@ -41,10 +43,16 @@ public class TestModelVMNode extends AbstractVMNode implements IRootVMNode, IEle final private static PropertiesBasedLabelProvider fLabelProvider = new PropertiesBasedLabelProvider(); { + LabelColumnInfo idLabelInfo = new LabelColumnInfo(new LabelAttribute[] { + new LabelText("{0}", new String[] { PROP_TEST_ELEMENT_LABEL }) + }); + + fLabelProvider.setColumnInfo(PropertiesBasedLabelProvider.ID_COLUMN_NO_COLUMNS, idLabelInfo); + fLabelProvider.setColumnInfo(TestModelCachingVMProvider.COLUMN_ID, idLabelInfo); fLabelProvider.setColumnInfo( - PropertiesBasedLabelProvider.ID_COLUMN_NO_COLUMNS, - new LabelColumnInfo(new LabelAttribute[] { - new LabelText("{0}", new String[] { PROP_TEST_ELEMENT_LABEL }) + TestModelCachingVMProvider.COLUMN_FORMATTED_VALUE, + new LabelColumnInfo(new LabelAttribute[] { + new FormattedValueLabelText() })); } @@ -52,7 +60,7 @@ public class TestModelVMNode extends AbstractVMNode implements IRootVMNode, IEle fLabelProvider.update(updates); } - public TestModelVMNode(IVMProvider provider) { + public TestModelVMNode(AbstractVMProvider provider) { super(provider); } @@ -123,7 +131,7 @@ public class TestModelVMNode extends AbstractVMNode implements IRootVMNode, IEle } public void createRootDelta(Object rootObject, Object event, DataRequestMonitor<VMDelta> rm) { - rm.setStatus(new Status(IStatus.ERROR, TestDsfVMPlugin.PLUGIN_ID, IDsfStatusConstants.NOT_SUPPORTED, "Not implemented", null)); + rm.setStatus(new Status(IStatus.ERROR, DsfTestPlugin.PLUGIN_ID, IDsfStatusConstants.NOT_SUPPORTED, "Not implemented", null)); rm.done(); } diff --git a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/TestModelVMProvider.java b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/TestModelVMProvider.java index b8265593367..a53186d1b8e 100644 --- a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/TestModelVMProvider.java +++ b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/TestModelVMProvider.java @@ -19,9 +19,10 @@ import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta; import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext; /** - * + * @since 2.2 */ public class TestModelVMProvider extends AbstractVMProvider { + public TestModelVMProvider(AbstractVMAdapter adapter, IPresentationContext context) { super(adapter, context); diff --git a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/VirtualViewerPerformanceTests.java b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/VirtualViewerPerformanceTests.java index 072fa267240..91cca156730 100644 --- a/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/VirtualViewerPerformanceTests.java +++ b/dsf/org.eclipse.cdt.tests.dsf/src/org/eclipse/cdt/tests/dsf/vm/VirtualViewerPerformanceTests.java @@ -17,7 +17,7 @@ import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; /** - * @since 3.6 + * @since 2.2 */ public class VirtualViewerPerformanceTests extends PerformanceTests { |