/*******************************************************************************
* Copyright (c) 2010, 2015 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.Dictionary;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.concurrent.RejectedExecutionException;
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.ImmediateCountingRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.ImmediateDataRequestMonitor;
import org.eclipse.cdt.dsf.datamodel.IDMContext;
import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext;
import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionGroupDMContext;
import org.eclipse.cdt.dsf.debug.service.IRegisters;
import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterDMContext;
import org.eclipse.cdt.dsf.debug.service.IRegisters.IRegisterDMData;
import org.eclipse.cdt.dsf.debug.ui.viewmodel.SimpleMapPersistable;
import org.eclipse.cdt.dsf.debug.ui.viewmodel.update.ElementFormatEvent;
import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin;
import org.eclipse.cdt.dsf.service.DsfServiceEventHandler;
import org.eclipse.cdt.dsf.service.DsfServicesTracker;
import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMProvider;
import org.eclipse.cdt.dsf.ui.viewmodel.IVMContext;
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.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.ui.IWorkbenchPart;
import org.osgi.framework.Filter;
import org.osgi.framework.InvalidSyntaxException;
/**
* Default implementation of the {@link IElementFormatProvider}. It can be
* used within any {@link IVMProvider} to store and persist number-formats
* selected by user for different elements.
*
* @since 2.5
*/
public class ElementNumberFormatProvider implements IElementFormatProvider
{
private static final String ELEMENT_FORMAT_PERSISTABLE_PROPERTY = "org.eclipse.cdt.dsf.ui.elementFormatPersistable"; //$NON-NLS-1$
private static final String FILTER_PROVIDER_ID = ElementNumberFormatProvider.class.getName() + ".eventFilter"; //$NON-NLS-1$
private final IVMProvider fVMProvider;
private final DsfSession fSession;
private final Dictionary<String, String> fFilterProperties = new Hashtable<>();
public ElementNumberFormatProvider(IVMProvider vmProvider, DsfSession session) {
fVMProvider = vmProvider;
fSession = session;
initialize();
}
protected void initialize() {
IPresentationContext presentationCtx = getVMProvider().getPresentationContext();
IWorkbenchPart part = presentationCtx.getPart();
String provider;
if (part != null) {
// Use an id that is unique to the instance of the view
// Note that although each view, including cloned ones, has its own presentation context,
// the presentation context id returned by getPresentationContext().getId() is the
// same for cloned views even though the presentation context itself is different.
// To get a unique id for each cloned view we can use the title of the view.
provider = part.getTitle();
} else {
// In some cases, we are not dealing with a part, e.g., the hover.
// In this case, use the presentation context id directly.
// Note that the hover will probably not provide per-element formating,
// but some extenders may choose to do so.
provider = getVMProvider().getPresentationContext().getId();
}
// Create the filter properties targeted at our provider, to be used when sending events
fFilterProperties.put(FILTER_PROVIDER_ID, provider);
// Properly formatted OSGI filter string aimed at our provider
String filterStr = "(&(" + FILTER_PROVIDER_ID + "=" + provider + "))"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
try {
final Filter filter = DsfUIPlugin.getBundleContext().createFilter(filterStr);
fSession.getExecutor().execute(new DsfRunnable() {
@Override
public void run() {
// Only listen to events that are aimed at our provider by using a filter.
// That avoids updating ourselves for an event that was triggered by another view.
fSession.addServiceEventListener(ElementNumberFormatProvider.this, filter);
}
});
} catch (InvalidSyntaxException e) {
assert false : e.getMessage();
} catch (RejectedExecutionException e) {
}
}
public void dispose() {
try {
fSession.getExecutor().execute(new DsfRunnable() {
@Override
public void run() {
fSession.removeServiceEventListener(ElementNumberFormatProvider.this);
}
});
} catch (RejectedExecutionException e) {
}
}
@DsfServiceEventHandler
public final void eventDispatched(ElementFormatEvent event) {
if (getVMProvider() instanceof AbstractVMProvider) {
((AbstractVMProvider)getVMProvider()).handleEvent(event);
}
}
private IVMProvider getVMProvider() {
return fVMProvider;
}
@Override
public void getActiveFormat(IPresentationContext context, IVMNode node, Object viewerInput, final TreePath elementPath,
final DataRequestMonitor<String> rm)
{
getElementKey(
viewerInput, elementPath,
new ImmediateDataRequestMonitor<String>(rm) {
@Override
protected void handleSuccess() {
SimpleMapPersistable<String> persistable = getPersistable();
rm.done(persistable.getValue(getData()));
}
});
}
@Override
public void setActiveFormat(IPresentationContext context, IVMNode[] node, Object viewerInput,
TreePath[] elementPaths, final String format)
{
final HashSet<Object> elementsToRefresh = new HashSet<>();
final CountingRequestMonitor crm = new ImmediateCountingRequestMonitor() {
@Override
protected void handleCompleted() {
if (!elementsToRefresh.isEmpty()) {
// Send the event to all DSF sessions as they share the same view and the
// change of format will affect them as well. This is because they key
// we use from this implementation of getElementKey() is not specific to
// a session (and should not be if we want to have proper persistence).
for (DsfSession session : DsfSession.getActiveSessions()) {
// Use the filterProperties to specify that this event only impacts the current view.
session.dispatchEvent(new ElementFormatEvent(elementsToRefresh, 1), fFilterProperties);
}
}
}
};
for (final TreePath path : elementPaths) {
getElementKey(
viewerInput, path,
new ImmediateDataRequestMonitor<String>(crm) {
@Override
protected void handleSuccess() {
SimpleMapPersistable<String> persistable = getPersistable();
persistable.setValue(getData(), format);
elementsToRefresh.add(path.getLastSegment());
crm.done();
}
});
}
crm.setDoneCount(elementPaths.length);
}
@Override
public boolean supportFormat(IVMContext context) {
if (context instanceof IDMVMContext) {
// The expressions view supports expression groups, which have no value,
// so we should not support formatting for expression groups.
if (((IDMVMContext)context).getDMContext() instanceof IExpressionGroupDMContext) {
return false;
}
}
return context instanceof IFormattedValueVMContext;
}
// We do not make the element key session-specific or else when we start a new session for the same
// program, the format we chose will not be persisted. Instead, make the format change valid for
// any session, even if other sessions run a different program. The idea is that a user usually
// names her variables similarly so the chosen format should apply properly anyway.
protected void getElementKey(Object viewerInput, TreePath elementPath, final DataRequestMonitor<String> rm) {
Object element = elementPath.getLastSegment();
if (element instanceof IDMVMContext) {
final IDMContext dmc = ((IDMVMContext)element).getDMContext();
if (dmc instanceof IExpressionDMContext) {
rm.done(((IExpressionDMContext)dmc).getExpression());
return;
} else if (dmc instanceof IRegisterDMContext) {
fSession.getExecutor().execute(new DsfRunnable() {
@Override
public void run() {
DsfServicesTracker tracker = new DsfServicesTracker(DsfUIPlugin.getBundleContext(), fSession.getId());
IRegisters regService = tracker.getService(IRegisters.class);
tracker.dispose();
regService.getRegisterData((IRegisterDMContext)dmc, new ImmediateDataRequestMonitor<IRegisterDMData>(rm) {
@Override
protected void handleSuccess() {
rm.done(getData().getName());
}
});
}
});
return;
}
}
rm.done(new Status(IStatus.ERROR, DsfUIPlugin.PLUGIN_ID, IDsfStatusConstants.INVALID_HANDLE, "Cannot calculate peristable key for element: " + element, null)); //$NON-NLS-1$
}
@SuppressWarnings("unchecked")
protected SimpleMapPersistable<String> getPersistable() {
Object p = getVMProvider().getPresentationContext().getProperty(ELEMENT_FORMAT_PERSISTABLE_PROPERTY);
if (p instanceof SimpleMapPersistable) {
return (SimpleMapPersistable<String>)p;
} else {
SimpleMapPersistable<String> persistable = new SimpleMapPersistable<>(String.class);
getVMProvider().getPresentationContext().setProperty(ELEMENT_FORMAT_PERSISTABLE_PROPERTY, persistable);
return persistable;
}
}
}