/*******************************************************************************
* Copyright (c) 2011 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.tm.te.tcf.locator.nodes;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.core.runtime.Assert;
import org.eclipse.tm.tcf.protocol.IPeer;
import org.eclipse.tm.tcf.protocol.Protocol;
import org.eclipse.tm.te.runtime.interfaces.workingsets.IWorkingSetElement;
import org.eclipse.tm.te.runtime.properties.PropertiesContainer;
import org.eclipse.tm.te.tcf.locator.interfaces.IModelListener;
import org.eclipse.tm.te.tcf.locator.interfaces.nodes.ILocatorModel;
import org.eclipse.tm.te.tcf.locator.interfaces.nodes.IPeerModel;
import org.eclipse.tm.te.tcf.locator.interfaces.nodes.IPeerModelProperties;
/**
* Default peer model implementation.
*/
public class PeerModel extends PropertiesContainer implements IPeerModel, IWorkingSetElement {
// Reference to the parent locator model
private final ILocatorModel parentModel;
// Reference to the element id (cached for performance optimization)
private String elementId;
/**
* Constructor.
*
* @param parent The parent locator model. Must not be <code>null</code>.
*/
public PeerModel(ILocatorModel parent) {
this(parent, null);
}
/**
* Constructor.
*
* @param parent The parent locator model. Must not be <code>null</code>.
* @param peer The peer or <code>null</code>.
*/
public PeerModel(ILocatorModel parent, IPeer peer) {
super();
Assert.isNotNull(parent);
parentModel = parent;
// Set the default properties before enabling the change events.
// The properties changed listeners should not be called from the
// constructor.
setProperty(IPeerModelProperties.PROP_INSTANCE, peer);
// Initialize the element id
elementId = peer.getID();
Assert.isNotNull(elementId);
// Enable change events
setChangeEventsEnabled(true);
}
/* (non-Javadoc)
* @see org.eclipse.tm.te.runtime.properties.PropertiesContainer#checkThreadAccess()
*/
@Override
protected final boolean checkThreadAccess() {
return Protocol.isDispatchThread();
}
/* (non-Javadoc)
* @see org.eclipse.tm.te.tcf.locator.interfaces.nodes.IPeerModel#getModel()
*/
@Override
public ILocatorModel getModel() {
return (ILocatorModel)getAdapter(ILocatorModel.class);
}
/* (non-Javadoc)
* @see org.eclipse.tm.te.tcf.locator.interfaces.nodes.IPeerModel#getPeer()
*/
@Override
public IPeer getPeer() {
return (IPeer)getAdapter(IPeer.class);
}
/* (non-Javadoc)
* @see org.eclipse.tm.te.runtime.interfaces.workingsets.IWorkingSetElement#getElementId()
*/
@Override
public String getElementId() {
return elementId;
}
/* (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 Object[] object = new Object[1];
if (Protocol.isDispatchThread()) {
object[0] = doGetAdapter(adapter);
} else {
Protocol.invokeAndWait(new Runnable() {
@Override
public void run() {
object[0] = doGetAdapter(adapter);
}
});
}
return object[0] != null ? object[0] : 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 (adapter.isAssignableFrom(ILocatorModel.class)) {
return parentModel;
}
Object peer = getProperty(IPeerModelProperties.PROP_INSTANCE);
if (peer != null && adapter.isAssignableFrom(peer.getClass())) {
return peer;
}
return null;
}
/* (non-Javadoc)
* @see org.eclipse.tm.te.runtime.properties.PropertiesContainer#toString()
*/
@Override
public String toString() {
final StringBuilder buffer = new StringBuilder(getClass().getSimpleName());
if (Protocol.isDispatchThread()) {
IPeer peer = getPeer();
buffer.append(": id=" + peer.getID()); //$NON-NLS-1$
buffer.append(", name=" + peer.getName()); //$NON-NLS-1$
} else {
Protocol.invokeAndWait(new Runnable() {
@Override
public void run() {
IPeer peer = getPeer();
buffer.append(": id=" + peer.getID()); //$NON-NLS-1$
buffer.append(", name=" + peer.getName()); //$NON-NLS-1$
}
});
}
buffer.append(", " + super.toString()); //$NON-NLS-1$
return buffer.toString();
}
/* (non-Javadoc)
* @see org.eclipse.tm.te.runtime.properties.PropertiesContainer#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (obj instanceof PeerModel) {
return getElementId().equals(((PeerModel)obj).getElementId());
}
return super.equals(obj);
}
/* (non-Javadoc)
* @see org.eclipse.tm.te.runtime.properties.PropertiesContainer#hashCode()
*/
@Override
public int hashCode() {
return getElementId().hashCode();
}
/* (non-Javadoc)
* @see org.eclipse.tm.te.runtime.properties.PropertiesContainer#getProperties()
*/
@Override
public Map<String, Object> getProperties() {
Map<String, Object> properties = new HashMap<String, Object>(super.getProperties());
if (getPeer() != null) properties.putAll(getPeer().getAttributes());
return Collections.unmodifiableMap(new HashMap<String, Object>(properties));
}
/* (non-Javadoc)
* @see org.eclipse.tm.te.runtime.properties.PropertiesContainer#getProperty(java.lang.String)
*/
@Override
public Object getProperty(String key) {
Object property = super.getProperty(key);
if (property == null && getPeer() != null && getPeer().getAttributes().containsKey(key)) {
property = getPeer().getAttributes().get(key);
}
return property;
}
/* (non-Javadoc)
* @see org.eclipse.tm.te.runtime.properties.PropertiesContainer#postSetProperties(java.util.Map)
*/
@Override
protected void postSetProperties(Map<String, Object> properties) {
Assert.isTrue(checkThreadAccess(), "Illegal Thread Access"); //$NON-NLS-1$
Assert.isNotNull(properties);
Assert.isNotNull(getPeer());
// New properties applied. Update the element id
elementId = getPeer().getID();
Assert.isNotNull(elementId);
if (changeEventsEnabled()) {
final IModelListener[] listeners = parentModel.getListener();
if (listeners.length > 0) {
Protocol.invokeLater(new Runnable() {
@Override
@SuppressWarnings("synthetic-access")
public void run() {
for (IModelListener listener : listeners) {
listener.peerModelChanged(parentModel, PeerModel.this);
}
}
});
}
}
}
/* (non-Javadoc)
* @see org.eclipse.tm.te.runtime.properties.PropertiesContainer#postSetProperty(java.lang.String, java.lang.Object, java.lang.Object)
*/
@Override
public void postSetProperty(String key, Object value, Object oldValue) {
Assert.isTrue(checkThreadAccess(), "Illegal Thread Access"); //$NON-NLS-1$
Assert.isNotNull(key);
Assert.isNotNull(getPeer());
// If the peer instance changed, update the element id
if (IPeerModelProperties.PROP_INSTANCE.equals(key)) {
elementId = getPeer().getID();
Assert.isNotNull(elementId);
}
// Notify registered listeners that the peer changed. Property
// changes for property slots ending with ".silent" are suppressed.
if (changeEventsEnabled() && !key.endsWith(".silent")) { //$NON-NLS-1$
final IModelListener[] listeners = parentModel.getListener();
if (listeners.length > 0) {
Protocol.invokeLater(new Runnable() {
@Override
@SuppressWarnings("synthetic-access")
public void run() {
for (IModelListener listener : listeners) {
listener.peerModelChanged(parentModel, PeerModel.this);
}
}
});
}
}
}
}