/*******************************************************************************
* Copyright (c) 2012, 2015 Wind River Systems, Inc. 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.tcf.te.tcf.processes.ui.navigator.runtime;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.ITreeViewerListener;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.tcf.protocol.Protocol;
import org.eclipse.tcf.services.ISysMonitor;
import org.eclipse.tcf.services.ISysMonitor.SysMonitorContext;
import org.eclipse.tcf.te.runtime.callback.Callback;
import org.eclipse.tcf.te.runtime.events.ChangeEvent;
import org.eclipse.tcf.te.runtime.events.EventManager;
import org.eclipse.tcf.te.runtime.model.PendingOperationModelNode;
import org.eclipse.tcf.te.runtime.model.interfaces.IContainerModelNode;
import org.eclipse.tcf.te.runtime.model.interfaces.contexts.IAsyncRefreshableCtx;
import org.eclipse.tcf.te.runtime.model.interfaces.contexts.IAsyncRefreshableCtx.QueryState;
import org.eclipse.tcf.te.runtime.model.interfaces.contexts.IAsyncRefreshableCtx.QueryType;
import org.eclipse.tcf.te.tcf.core.model.interfaces.services.IModelRefreshService;
import org.eclipse.tcf.te.tcf.locator.interfaces.nodes.IPeerNode;
import org.eclipse.tcf.te.tcf.processes.core.model.ModelManager;
import org.eclipse.tcf.te.tcf.processes.core.model.interfaces.IPendingOperationNode;
import org.eclipse.tcf.te.tcf.processes.core.model.interfaces.IProcessContextNode;
import org.eclipse.tcf.te.tcf.processes.core.model.interfaces.runtime.IRuntimeModel;
import org.eclipse.tcf.te.tcf.processes.core.model.nodes.PendingOperationNode;
import org.eclipse.tcf.te.tcf.processes.ui.navigator.events.TreeViewerListener;
/**
* Runtime model content provider delegate implementation.
*/
public class ContentProvider implements ITreeContentProvider {
private final static Object[] NO_ELEMENTS = new Object[0];
// Reference to the tree listener
/* default */ ITreeViewerListener listener = null;
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.ITreeContentProvider#getChildren(java.lang.Object)
*/
@Override
public Object[] getChildren(final Object parentElement) {
Object[] children = NO_ELEMENTS;
// If the parent element is a peer model node, than return
// the children of the corresponding runtime model.
if (parentElement instanceof IPeerNode) {
IRuntimeModel model = ModelManager.getRuntimeModel((IPeerNode)parentElement);
return isRuntimeModelNodeVisible() ? new Object[] { model } : getChildren(model);
}
// If it is the runtime model, get the process contexts
if (parentElement instanceof IRuntimeModel) {
// Get the asynchronous refresh context adapter
final IAsyncRefreshableCtx refreshable = (IAsyncRefreshableCtx)((IRuntimeModel)parentElement).getAdapter(IAsyncRefreshableCtx.class);
if (refreshable != null) {
if (refreshable.getQueryState(QueryType.CHILD_LIST).equals(QueryState.PENDING)) {
// Mark the refresh as in progress
refreshable.setQueryState(QueryType.CHILD_LIST, QueryState.IN_PROGRESS);
// Create a new pending operation node and associate it with the refreshable
PendingOperationModelNode pendingNode = new PendingOperationNode();
pendingNode.setParent((IRuntimeModel)parentElement);
refreshable.setPendingOperationNode(pendingNode);
// Cast to the model instance
final IRuntimeModel model = (IRuntimeModel)parentElement;
// Create the runnable
Runnable runnable = new Runnable() {
@Override
public void run() {
// Don't send change events while refreshing
final boolean changed = model.setChangeEventsEnabled(false);
// Initiate the refresh of the model
model.getService(IModelRefreshService.class).refresh(new Callback() {
@Override
protected void internalDone(Object caller, IStatus status) {
// Mark the refresh as done
refreshable.setQueryState(QueryType.CHILD_LIST, QueryState.DONE);
// Reset the pending operation node
refreshable.setPendingOperationNode(null);
// Re-enable the change events if they had been enabled before
if (changed) model.setChangeEventsEnabled(true);
// Trigger a refresh of the view content.
ChangeEvent event = new ChangeEvent(model, IContainerModelNode.NOTIFY_CHANGED, null, null);
EventManager.getInstance().fireEvent(event);
}
});
}
};
Protocol.invokeLater(runnable);
// Return the pending operation node
return new Object[] { refreshable.getPendingOperationNode() };
}
else if (refreshable.getQueryState(QueryType.CHILD_LIST).equals(QueryState.IN_PROGRESS) && ((IRuntimeModel)parentElement).getAutoRefreshInterval() == 0) {
// Refresh is still running -> return the pending operation node (if set)
return refreshable.getPendingOperationNode() != null ? new Object[] { refreshable.getPendingOperationNode() } : NO_ELEMENTS;
}
}
children = ((IRuntimeModel)parentElement).getChildren(IProcessContextNode.class).toArray();
}
// If it is a system context, get the system context children
else if (parentElement instanceof IProcessContextNode) {
// Get the asynchronous refresh context adapter
final IAsyncRefreshableCtx refreshable = (IAsyncRefreshableCtx)((IProcessContextNode)parentElement).getAdapter(IAsyncRefreshableCtx.class);
if (refreshable != null) {
if (refreshable.getQueryState(QueryType.CHILD_LIST).equals(QueryState.PENDING)) {
// Mark the refresh as in progress
refreshable.setQueryState(QueryType.CHILD_LIST, QueryState.IN_PROGRESS);
// Create a new pending operation node and associate it with the refreshable
PendingOperationModelNode pendingNode = new PendingOperationNode();
pendingNode.setParent((IProcessContextNode)parentElement);
refreshable.setPendingOperationNode(pendingNode);
// Cast to the context node instance
final IProcessContextNode node = (IProcessContextNode)parentElement;
// Create the runnable
Runnable runnable = new Runnable() {
@Override
public void run() {
// Don't send change events while refreshing
final boolean changed = node.setChangeEventsEnabled(false);
// Determine the runtime model
IRuntimeModel model = node.getParent(IRuntimeModel.class);
if (model != null) {
// Initiate the refresh of the node
model.getService(IModelRefreshService.class).refresh(node, new Callback() {
@Override
protected void internalDone(Object caller, IStatus status) {
// Mark the refresh as done
refreshable.setQueryState(QueryType.CHILD_LIST, QueryState.DONE);
// Reset the pending operation node
refreshable.setPendingOperationNode(null);
// Re-enable the change events if they had been enabled before
if (changed) node.setChangeEventsEnabled(true);
// Trigger a refresh of the view content
ChangeEvent event = new ChangeEvent(node, IContainerModelNode.NOTIFY_CHANGED, null, null);
EventManager.getInstance().fireEvent(event);
}
});
}
}
};
Protocol.invokeLater(runnable);
// Return the pending operation node
return new Object[] { refreshable.getPendingOperationNode() };
}
else if (refreshable.getQueryState(QueryType.CHILD_LIST).equals(QueryState.IN_PROGRESS)) {
final AtomicReference<IRuntimeModel> model = new AtomicReference<IRuntimeModel>();
Runnable runnable = new Runnable() {
@Override
public void run() {
model.set(((IProcessContextNode)parentElement).getParent(IRuntimeModel.class));
}
};
if (Protocol.isDispatchThread()) runnable.run();
else Protocol.invokeAndWait(runnable);
if (model.get() == null || model.get().getAutoRefreshInterval() == 0) {
// Refresh is still running -> return the pending operation node (if set)
return refreshable.getPendingOperationNode() != null ? new Object[] { refreshable.getPendingOperationNode() } : NO_ELEMENTS;
}
}
}
children = ((IProcessContextNode)parentElement).getChildren(IProcessContextNode.class).toArray();
}
return children;
}
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.ITreeContentProvider#getParent(java.lang.Object)
*/
@Override
public Object getParent(final Object element) {
final AtomicReference<Object> parent = new AtomicReference<Object>();
// The parent of the runtime model is the peer model node
if (element instanceof IRuntimeModel) {
Runnable runnable = new Runnable() {
@Override
public void run() {
parent.set(((IRuntimeModel)element).getPeerNode());
}
};
if (Protocol.isDispatchThread()) runnable.run();
else Protocol.invokeAndWait(runnable);
}
else if (element instanceof IProcessContextNode) {
Runnable runnable = new Runnable() {
@Override
public void run() {
parent.set(((IProcessContextNode)element).getParent());
}
};
if (Protocol.isDispatchThread()) runnable.run();
else Protocol.invokeAndWait(runnable);
}
else if (element instanceof IPendingOperationNode) {
parent.set(((IPendingOperationNode)element).getParent());
}
if (parent.get() instanceof IRuntimeModel) {
parent.set(getParent(parent.get()));
}
return parent.get();
}
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(java.lang.Object)
*/
@Override
public boolean hasChildren(final Object element) {
// Default to "no children". This applies to IProcessContextNode
// and IPendingOperationNode elements.
boolean hasChildren = false;
if (element instanceof IRuntimeModel) {
IRuntimeModel model = ((IRuntimeModel)element);
// Get the asynchronous refresh context adapter
final IAsyncRefreshableCtx refreshable = (IAsyncRefreshableCtx)model.getAdapter(IAsyncRefreshableCtx.class);
if (refreshable != null && refreshable.getQueryState(QueryType.CHILD_LIST).equals(QueryState.PENDING)) {
hasChildren = true;
} else {
hasChildren = model.hasChildren();
}
} else if (element instanceof IProcessContextNode) {
final IProcessContextNode context = (IProcessContextNode)element;
// Get the asynchronous refresh context adapter
final IAsyncRefreshableCtx refreshable = (IAsyncRefreshableCtx)context.getAdapter(IAsyncRefreshableCtx.class);
if (refreshable != null && refreshable.getQueryState(QueryType.CHILD_LIST).equals(QueryState.PENDING)) {
hasChildren = true;
} else {
// We need the real children list to determine if we have children
final Object[] children = getChildren(context);
if (children.length == 1 && children[0] instanceof IProcessContextNode) {
// Apply the single thread filter
final AtomicBoolean selected = new AtomicBoolean(true);
Runnable runnable = new Runnable() {
@Override
public void run() {
IProcessContextNode child = (IProcessContextNode)children[0];
if (child != null && context.getSysMonitorContext() != null && child.getSysMonitorContext() != null &&
context.getSysMonitorContext().getPID() == child.getSysMonitorContext().getPID()) {
SysMonitorContext smc = context.getSysMonitorContext();
if (Integer.valueOf(ISysMonitor.EXETYPE_KERNEL).equals(smc.getProperties().get(ISysMonitor.PROP_EXETYPE))) {
selected.set(false);
} else if (context.getName() != null) {
selected.set(!context.getName().equals(child.getName()));
}
else if (child.getName() != null) {
selected.set(!child.getName().equals(context.getName()));
}
}
}
};
Assert.isTrue(!Protocol.isDispatchThread());
Protocol.invokeAndWait(runnable);
hasChildren = selected.get();
} else {
hasChildren = children.length > 0;
}
}
}
else if (element instanceof IPeerNode) {
hasChildren = true;
}
return hasChildren;
}
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
*/
@Override
public Object[] getElements(Object inputElement) {
return getChildren(inputElement);
}
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.IContentProvider#dispose()
*/
@Override
public void dispose() {
}
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object)
*/
@Override
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
// Initialize the tree listener if necessary
if (listener == null && viewer instanceof TreeViewer) {
final TreeViewer treeViewer = (TreeViewer) viewer;
listener = new TreeViewerListener();
treeViewer.addTreeListener(listener);
treeViewer.getTree().addDisposeListener(new DisposeListener() {
@Override
public void widgetDisposed(DisposeEvent e) {
if (listener != null) {
treeViewer.removeTreeListener(listener);
listener = null;
}
}
});
}
}
/**
* Returns if or if not the root node, the runtime model node, is
* visible.
*
* @return <code>True</code> if the runtime model node is visible, <code>false</code> otherwise.
*/
protected boolean isRuntimeModelNodeVisible() {
return true;
}
}