blob: c57b7e435cf62136ec037fd4156b71aea65b5c6f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2014 Wind River Systems and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Wind River Systems - initial API and implementation
* Winnie Lai (Texas Instruments) - Individual Element Number Format (Bug 202556)
*******************************************************************************/
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.HashSet;
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.IVMProvider;
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 IElementFormatProvider fElementFormatProvider;
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();
IVMProvider vmprovider = fNode.getVMProvider();
fElementFormatProvider = vmprovider instanceof IElementFormatProvider ? (IElementFormatProvider) vmprovider
: null;
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) {
retrieveElementActiveFormat(updates,
new DataRequestMonitor<Map<IPropertiesUpdate, String>>(ImmediateExecutor.getInstance(), rm) {
@Override
protected void handleCompleted() {
final Map<IPropertiesUpdate, String> elementFormatMap = getData();
final Map<IPropertiesUpdate, String[]> cachedAvailableFormatsMap = calcCachedAvailableFormatsMap(
updates);
if ((cachedAvailableFormatsMap != null && cachedAvailableFormatsMap.size() == updates.length)) {
// All updates were satisfied by the cache.
doUpdateWithAvailableFormats(updates, cachedAvailableFormatsMap, elementFormatMap, 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, //$NON-NLS-1$
null));
rm.done();
return;
}
try {
service.getExecutor().execute(new DsfRunnable() {
@Override
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,
elementFormatMap, 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 void retrieveElementActiveFormat(final IPropertiesUpdate updates[],
final DataRequestMonitor<Map<IPropertiesUpdate, String>> rm) {
if (fElementFormatProvider == null) {
rm.setData(new HashMap<IPropertiesUpdate, String>(0));
rm.done();
return;
}
Map<IPropertiesUpdate, String> cachedMap = null;
HashSet<IPropertiesUpdate> outstanding = null;
for (IPropertiesUpdate update : updates) {
if (isElementFormatPropertyNeeded(update) == false) {
continue;
}
String active = null;
ICacheEntry cacheEntry = fCache.getCacheEntry(fNode, update.getViewerInput(), update.getElementPath());
if (cacheEntry != null && cacheEntry.getProperties() != null) {
active = (String) cacheEntry.getProperties().get(PROP_ACTIVE_FORMAT);
}
if (active != null) {
if (cachedMap == null) {
cachedMap = new HashMap<>(updates.length * 4 / 3);
}
cachedMap.put(update, active);
} else {
if (outstanding == null) {
outstanding = new HashSet<>(updates.length * 4 / 3);
}
outstanding.add(update);
}
}
if (outstanding == null || outstanding.size() == 0) {
rm.setData(cachedMap == null ? new HashMap<IPropertiesUpdate, String>(0) : cachedMap);
rm.done();
return;
}
if (cachedMap == null) {
cachedMap = new HashMap<>(updates.length * 4 / 3);
}
final Map<IPropertiesUpdate, String> elementFormatMap = Collections.synchronizedMap(cachedMap);
rm.setData(elementFormatMap);
final CountingRequestMonitor countingRm = new CountingRequestMonitor(ImmediateExecutor.getInstance(), rm);
int count = 0;
for (final IPropertiesUpdate update : outstanding) {
fElementFormatProvider.getActiveFormat(update.getPresentationContext(), fNode, update.getViewerInput(),
update.getElementPath(),
new ViewerDataRequestMonitor<String>(ImmediateExecutor.getInstance(), update) {
@Override
protected void handleCompleted() {
if (isSuccess()) {
String active = this.getData();
if (update.getProperties().contains(PROP_ACTIVE_FORMAT)) {
update.setProperty(PROP_ACTIVE_FORMAT, active);
}
elementFormatMap.put(update, active);
}
countingRm.done();
}
});
count++;
}
countingRm.setDoneCount(count);
}
/**
* 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<>(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<>(
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 Map<IPropertiesUpdate, String> elementFormatMap, final RequestMonitor rm) {
final List<IPropertiesUpdate> outstandingUpdates = new ArrayList<>(updates.length);
final Map<IPropertiesUpdate, List<String>> requestedFormatsMap = new HashMap<>(updates.length * 4 / 3);
final Map<IPropertiesUpdate, String> activeFormatsMap = new HashMap<>(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,
elementFormatMap);
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() {
@Override
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.
* @param elementFormatMap The map of element active format.
* @return The active format, or null if active format not requested in
* update.
*/
private String calcActiveFormat(IPropertiesUpdate update, String preferredFormat,
Map<IPropertiesUpdate, String[]> availableFormatsMap, Map<IPropertiesUpdate, String> elementFormatMap) {
String[] availableFormats = availableFormatsMap.get(update);
if (availableFormats != null && availableFormats.length != 0) {
String elementFormat = elementFormatMap.get(update);
if (elementFormat != null && isFormatAvailable(elementFormat, availableFormats)) {
return elementFormat;
}
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<>(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);
}
/**
* For each update, query the active format for the update's
* element...but only if necessary. It is necessary only if the
* update is asking what the active format is or is asking for the value
* of the element in that format.
* @param update
* @return true if needed
*/
private boolean isElementFormatPropertyNeeded(IPropertiesUpdate update) {
if (fElementFormatProvider == null)
return false;
return 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;
}
}