/*******************************************************************************
* Copyright (c) 2012, 2014 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.core.model.nodes;
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.tcf.protocol.Protocol;
import org.eclipse.tcf.services.IProcesses;
import org.eclipse.tcf.services.ISysMonitor;
import org.eclipse.tcf.services.ISysMonitor.SysMonitorContext;
import org.eclipse.tcf.te.core.interfaces.IConnectable;
import org.eclipse.tcf.te.core.interfaces.IFilterable;
import org.eclipse.tcf.te.runtime.model.ContainerModelNode;
import org.eclipse.tcf.te.runtime.model.contexts.AsyncRefreshableCtxAdapter;
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.IModel;
import org.eclipse.tcf.te.tcf.locator.interfaces.nodes.IPeerNode;
import org.eclipse.tcf.te.tcf.locator.interfaces.nodes.IPeerNodeProvider;
import org.eclipse.tcf.te.tcf.processes.core.model.interfaces.IProcessContextNode;
import org.eclipse.tcf.te.tcf.processes.core.model.interfaces.IProcessContextNodeProperties;
/**
* A process context node implementation.
*/
public class ProcessContextNode extends ContainerModelNode implements IProcessContextNode, IPeerNodeProvider, IFilterable {
// Reference to the agent side process context object
private IProcesses.ProcessContext pContext = null;
// Reference to the agent side system monitor context object
private ISysMonitor.SysMonitorContext sContext = null;
// The node type
private TYPE type = TYPE.Unknown;
// Context nodes needs asynchronous refreshes
private final IAsyncRefreshableCtx refreshableCtxAdapter = new AsyncRefreshableCtxAdapter();
/**
* Constructor.
*/
public ProcessContextNode() {
super();
setChangeEventsEnabled(true);
// No initial context query required
refreshableCtxAdapter.setQueryState(QueryType.CONTEXT, QueryState.DONE);
}
/* (non-Javadoc)
* @see org.eclipse.tcf.te.runtime.properties.PropertiesContainer#checkThreadAccess()
*/
@Override
protected boolean checkThreadAccess() {
return Protocol.isDispatchThread();
}
/* (non-Javadoc)
* @see org.eclipse.tcf.te.runtime.model.ModelNode#getName()
*/
@Override
public String getName() {
Assert.isTrue(checkThreadAccess(), "Illegal Thread Access"); //$NON-NLS-1$
// If the node is associated with an agent side context, take the name
// from the agent side context first
String name = pContext != null ? pContext.getName() : null;
// If the name is empty or the same as the context ID -> reset
if (name != null && ("".equals(name.trim()) || name.equals(pContext.getID()))) { //$NON-NLS-1$
name = null;
}
if (name != null && sContext != null && sContext.getProperties().containsKey(ISysMonitor.PROP_EXETYPE)) {
// in case of a kernel thread, use process context name
Object exeType = sContext.getProperties().get(ISysMonitor.PROP_EXETYPE);
if (Integer.valueOf(ISysMonitor.EXETYPE_KERNEL).equals(exeType))
return name;
}
// Take the last part of the image name if available
String file = sContext != null ? sContext.getFile() : null;
if (file != null && !"".equals(file)) { //$NON-NLS-1$
IPath path = new Path(file);
// If it is a typical Windows path with a drive letter, take only the
// last segment. In all other cases, take "file" as it is
if (path.getDevice() != null && path.getDevice().endsWith(":") && path.getDevice().length() == 2) { //$NON-NLS-1$
name = path.lastSegment();
} else {
name = file;
}
}
// Fallback to the context ID
if (name == null || "".equals(name.trim())) name = pContext != null ? pContext.getID() : null; //$NON-NLS-1$
// Fallback to the local node properties
if (name == null || "".equals(name.trim())) name = getStringProperty(IProcessContextNodeProperties.PROPERTY_NAME); //$NON-NLS-1$
if (name == null || "".equals(name.trim())) name = getStringProperty(IProcessContextNodeProperties.PROPERTY_ID); //$NON-NLS-1$
return name != null && !"".equals(name.trim()) ? name.trim() : super.getName(); //$NON-NLS-1$
}
/* (non-Javadoc)
* @see org.eclipse.tcf.te.tcf.processes.core.model.interfaces.IProcessContextNode#setType(org.eclipse.tcf.te.tcf.processes.core.model.interfaces.IProcessContextNode.TYPE)
*/
@Override
public void setType(TYPE type) {
Assert.isNotNull(type);
this.type = type;
}
/* (non-Javadoc)
* @see org.eclipse.tcf.te.tcf.processes.core.model.interfaces.IProcessContextNode#getType()
*/
@Override
public TYPE getType() {
return type;
}
/* (non-Javadoc)
* @see org.eclipse.tcf.te.tcf.processes.core.model.interfaces.IProcessContextNode#setContext(org.eclipse.tcf.services.IProcesses.ProcessContext)
*/
@Override
public final void setProcessContext(IProcesses.ProcessContext context) {
Assert.isTrue(checkThreadAccess(), "Illegal Thread Access"); //$NON-NLS-1$
this.pContext = context;
}
/* (non-Javadoc)
* @see org.eclipse.tcf.te.tcf.processes.core.model.interfaces.IProcessContextNode#getContext()
*/
@Override
public final IProcesses.ProcessContext getProcessContext() {
Assert.isTrue(checkThreadAccess(), "Illegal Thread Access"); //$NON-NLS-1$
return pContext;
}
/* (non-Javadoc)
* @see org.eclipse.tcf.te.tcf.processes.core.model.interfaces.IProcessContextNode#setSysMonitorContext(org.eclipse.tcf.services.ISysMonitor.SysMonitorContext)
*/
@Override
public void setSysMonitorContext(SysMonitorContext context) {
Assert.isTrue(checkThreadAccess(), "Illegal Thread Access"); //$NON-NLS-1$
this.sContext = context;
}
/* (non-Javadoc)
* @see org.eclipse.tcf.te.tcf.processes.core.model.interfaces.IProcessContextNode#getSysMonitorContext()
*/
@Override
public SysMonitorContext getSysMonitorContext() {
Assert.isTrue(checkThreadAccess(), "Illegal Thread Access"); //$NON-NLS-1$
return sContext;
}
/* (non-Javadoc)
* @see org.eclipse.tcf.te.tcf.locator.interfaces.nodes.IPeerNodeProvider#getPeerModel()
*/
@Override
public IPeerNode getPeerNode() {
return (IPeerNode)getAdapter(IPeerNode.class);
}
/* (non-Javadoc)
* @see org.eclipse.tcf.te.tcf.processes.core.model.interfaces.IProcessContextNode#isComplete()
*/
@Override
public boolean isComplete() {
Assert.isTrue(checkThreadAccess(), "Illegal Thread Access"); //$NON-NLS-1$
// Return true if one of the contexts is not null.
if (getProcessContext() != null || getSysMonitorContext() != null) {
return true;
}
boolean complete = true;
// ID is mandatory and must not be empty
String id = getStringProperty(IProcessContextNodeProperties.PROPERTY_ID);
complete &= id != null && !"".equals(id); //$NON-NLS-1$
return complete;
}
/* (non-Javadoc)
* @see org.eclipse.core.runtime.PlatformObject#getAdapter(java.lang.Class)
*/
@Override
public Object getAdapter(final Class adapter) {
// NOTE: The getAdapter(...) method can be invoked from many place and
// many threads where we cannot control the calls. Therefore, this
// method is allowed be called from any thread.
final AtomicReference<Object> object = new AtomicReference<Object>();
Runnable runnable = new Runnable() {
@Override
public void run() {
object.set(doGetAdapter(adapter));
}
};
if (Protocol.isDispatchThread()) runnable.run();
else Protocol.invokeAndWait(runnable);
return object.get() != null ? object.get() : super.getAdapter(adapter);
}
/**
* Returns an object which is an instance of the given class associated with this object.
* Returns <code>null</code> if no such object can be found.
* <p>
* This method must be called within the TCF dispatch thread!
*
* @param adapter The adapter class to look up.
* @return The adapter or <code>null</code>.
*/
protected Object doGetAdapter(Class<?> adapter) {
Assert.isTrue(checkThreadAccess(), "Illegal Thread Access"); //$NON-NLS-1$
if (IProcesses.ProcessContext.class.isAssignableFrom(adapter)) {
return pContext;
}
if (ISysMonitor.SysMonitorContext.class.isAssignableFrom(adapter)) {
return sContext;
}
if (IPeerNodeProvider.class.isAssignableFrom(adapter)) {
IModel model = getParent(IModel.class);
if (model instanceof IPeerNodeProvider) return model;
}
if (IPeerNode.class.isAssignableFrom(adapter) || IConnectable.class.isAssignableFrom(adapter)) {
IModel model = getParent(IModel.class);
if (model instanceof IPeerNodeProvider) return ((IPeerNodeProvider)model).getPeerNode();
}
if (IAsyncRefreshableCtx.class.isAssignableFrom(adapter)) {
return refreshableCtxAdapter;
}
return null;
}
/* (non-Javadoc)
* @see org.eclipse.tcf.te.runtime.model.ModelNode#toString()
*/
@Override
public String toString() {
final AtomicReference<String> toString = new AtomicReference<String>(super.toString());
Runnable runnable = new Runnable() {
@Override
public void run() {
StringBuilder buffer = new StringBuilder(toString.get());
buffer.deleteCharAt(buffer.length() - 1);
buffer.append(", system monitor properties="); //$NON-NLS-1$
buffer.append(getSysMonitorContext() != null ? getSysMonitorContext().toString() : "{}"); //$NON-NLS-1$
buffer.append(", process properties="); //$NON-NLS-1$
buffer.append(getProcessContext() != null ? getProcessContext().toString() : "{}"); //$NON-NLS-1$
buffer.append("}"); //$NON-NLS-1$
toString.set(buffer.toString());
}
};
if (Protocol.isDispatchThread()) runnable.run();
else Protocol.invokeAndWait(runnable);
return toString.get();
}
}