/*******************************************************************************
* Copyright (c) 2011, 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.ui.editor.sections;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.eclipse.core.runtime.Assert;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.TypedEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.tcf.protocol.IChannel;
import org.eclipse.tcf.protocol.IPeer;
import org.eclipse.tcf.protocol.Protocol;
import org.eclipse.tcf.te.core.interfaces.IConnectable;
import org.eclipse.tcf.te.runtime.interfaces.properties.IPropertiesContainer;
import org.eclipse.tcf.te.runtime.properties.PropertiesContainer;
import org.eclipse.tcf.te.tcf.core.Tcf;
import org.eclipse.tcf.te.tcf.core.interfaces.ITransportTypes;
import org.eclipse.tcf.te.tcf.core.peers.Peer;
import org.eclipse.tcf.te.tcf.locator.interfaces.nodes.IPeerNode;
import org.eclipse.tcf.te.tcf.locator.interfaces.nodes.IPeerNodeProperties;
import org.eclipse.tcf.te.tcf.ui.controls.CustomTransportPanel;
import org.eclipse.tcf.te.tcf.ui.controls.PipeTransportPanel;
import org.eclipse.tcf.te.tcf.ui.controls.TcpTransportPanel;
import org.eclipse.tcf.te.tcf.ui.editor.controls.TransportSectionTypeControl;
import org.eclipse.tcf.te.tcf.ui.editor.controls.TransportSectionTypePanelControl;
import org.eclipse.tcf.te.tcf.ui.nls.Messages;
import org.eclipse.tcf.te.ui.controls.interfaces.IWizardConfigurationPanel;
import org.eclipse.tcf.te.ui.forms.parts.AbstractSection;
import org.eclipse.tcf.te.ui.interfaces.data.IDataExchangeNode;
import org.eclipse.tcf.te.ui.interfaces.data.IDataExchangeNode3;
import org.eclipse.tcf.te.ui.swt.SWTControlUtil;
import org.eclipse.tcf.te.ui.views.editor.pages.AbstractEditorPage;
import org.eclipse.ui.forms.IManagedForm;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.Section;
/**
* Peer transport section implementation.
*/
public class TransportSection extends AbstractSection implements IDataExchangeNode {
// The section sub controls
private TransportSectionTypeControl transportTypeControl = null;
/* default */TransportSectionTypePanelControl transportTypePanelControl = null;
// Reference to the original data object
protected IPeerNode od;
// Reference to a copy of the original data
/* default */final IPropertiesContainer odc = new PropertiesContainer();
// Reference to the properties container representing the working copy for the section
/* default */final IPropertiesContainer wc = new PropertiesContainer();
/**
* Constructor.
*
* @param form The parent managed form. Must not be <code>null</code>.
* @param parent The parent composite. Must not be <code>null</code>.
*/
public TransportSection(IManagedForm form, Composite parent) {
super(form, parent, Section.DESCRIPTION);
createClient(getSection(), form.getToolkit());
}
/*
* (non-Javadoc)
* @see org.eclipse.ui.forms.AbstractFormPart#dispose()
*/
@Override
public void dispose() {
super.dispose();
if (transportTypeControl != null) {
transportTypeControl.dispose();
transportTypeControl = null;
}
if (transportTypePanelControl != null) {
transportTypePanelControl.dispose();
transportTypePanelControl = null;
}
}
/*
* (non-Javadoc)
* @see org.eclipse.tcf.te.ui.forms.parts.AbstractSection#getAdapter(java.lang.Class)
*/
@Override
public Object getAdapter(Class adapter) {
if (TransportSectionTypeControl.class.equals(adapter)) {
return transportTypeControl;
}
if (TransportSectionTypePanelControl.class.equals(adapter)) {
return transportTypePanelControl;
}
return super.getAdapter(adapter);
}
/*
* (non-Javadoc)
* @see
* org.eclipse.tcf.te.ui.forms.parts.AbstractSection#createClient(org.eclipse.ui.forms.widgets
* .Section, org.eclipse.ui.forms.widgets.FormToolkit)
*/
@Override
protected void createClient(Section section, FormToolkit toolkit) {
Assert.isNotNull(section);
Assert.isNotNull(toolkit);
// Configure the section
section.setText(Messages.TransportSection_title);
section.setDescription(Messages.TransportSection_description);
if (section.getParent().getLayout() instanceof GridLayout) {
section.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
}
// Create the section client
Composite client = createClientContainer(section, 2, toolkit);
Assert.isNotNull(client);
section.setClient(client);
// Create the transport type control
transportTypeControl = new TransportSectionTypeControl(this) {
@Override
public String[] getTransportTypes() {
List<String> types = new ArrayList<String>();
for (String type : super.getTransportTypes()) {
if (isTransportTypeSupported(type)) {
types.add(type);
}
}
return types.toArray(new String[types.size()]);
}
};
transportTypeControl.setFormToolkit(toolkit);
transportTypeControl.setAdjustBackgroundColor(true);
transportTypeControl.setupPanel(client);
createEmptySpace(client, 2, toolkit);
// The transport type specific controls are placed into a stack
transportTypePanelControl = new TransportSectionTypePanelControl(this);
// Create and add the panels
if (isTransportTypeSupported(ITransportTypes.TRANSPORT_TYPE_TCP)) {
TcpTransportPanel tcpTransportPanel = new TcpTransportPanel(transportTypePanelControl) {
@Override
protected boolean isAdjustBackgroundColor() {
return true;
}
@Override
protected boolean hasHistory() {
return true;
}
};
transportTypePanelControl.addConfigurationPanel(ITransportTypes.TRANSPORT_TYPE_TCP, tcpTransportPanel);
}
if (isTransportTypeSupported(ITransportTypes.TRANSPORT_TYPE_SSL)) {
TcpTransportPanel sslTransportPanel = new TcpTransportPanel(transportTypePanelControl) {
@Override
protected boolean isAdjustBackgroundColor() {
return true;
}
};
transportTypePanelControl.addConfigurationPanel(ITransportTypes.TRANSPORT_TYPE_SSL, sslTransportPanel);
}
if (isTransportTypeSupported(ITransportTypes.TRANSPORT_TYPE_PIPE)) {
PipeTransportPanel pipeTransportPanle = new PipeTransportPanel(transportTypePanelControl) {
@Override
protected boolean isAdjustBackgroundColor() {
return true;
}
};
transportTypePanelControl.addConfigurationPanel(ITransportTypes.TRANSPORT_TYPE_PIPE, pipeTransportPanle);
}
if (isTransportTypeSupported(ITransportTypes.TRANSPORT_TYPE_CUSTOM)) {
CustomTransportPanel customTransportPanel = new CustomTransportPanel(transportTypePanelControl) {
@Override
protected boolean isAdjustBackgroundColor() {
return true;
}
};
transportTypePanelControl.addConfigurationPanel(ITransportTypes.TRANSPORT_TYPE_CUSTOM, customTransportPanel);
}
// Setup the panel control
transportTypePanelControl.setupPanel(client, transportTypePanelControl.getConfigurationPanelIds(), toolkit);
GridData layoutData = new GridData(SWT.FILL, SWT.FILL, true, true);
layoutData.horizontalSpan = 2;
transportTypePanelControl.getPanel().setLayoutData(layoutData);
toolkit.adapt(transportTypePanelControl.getPanel());
transportTypePanelControl.showConfigurationPanel(transportTypeControl.getSelectedTransportType());
// Adjust the control enablement
updateEnablement();
// Mark the control update as completed now
setIsUpdating(false);
}
/**
* Override to control the availability of transport types.
*
* @param transportType
* @return <code>true</code> if the given transport type should is available.
*/
public boolean isTransportTypeSupported(String transportType) {
return true;
}
/**
* Indicates whether the sections parent page has become the active in the editor.
*
* @param active <code>True</code> if the parent page should be visible, <code>false</code>
* otherwise.
*/
public void setActive(boolean active) {
// If the parent page has become the active and it does not contain
// unsaved data, than fill in the data from the selected node
if (active) {
// Leave everything unchanged if the page is in dirty state
if (getManagedForm().getContainer() instanceof AbstractEditorPage && !((AbstractEditorPage) getManagedForm().getContainer()).isDirty()) {
Object node = ((AbstractEditorPage) getManagedForm().getContainer()).getEditorInputNode();
if (node instanceof IPeerNode) {
setupData((IPeerNode) node);
}
}
}
else {
// Evaluate the dirty state even if going inactive
dataChanged(null);
}
}
/* (non-Javadoc)
* @see org.eclipse.tcf.te.ui.interfaces.data.IDataExchangeNode#setupData(org.eclipse.tcf.te.runtime.interfaces.properties.IPropertiesContainer)
*/
@Override
public void setupData(IPropertiesContainer data) {
Assert.isNotNull(data);
// Mark the control update as in-progress now
setIsUpdating(true);
if (transportTypeControl != null) {
String transportType = data.getStringProperty(IPeer.ATTR_TRANSPORT_NAME);
if (transportType != null && !"".equals(transportType)) { //$NON-NLS-1$
transportTypeControl.setSelectedTransportType(transportType);
if (!transportTypeControl.getSelectedTransportType().equals(transportType)) {
transportType = ITransportTypes.TRANSPORT_TYPE_CUSTOM;
transportTypeControl.setSelectedTransportType(transportType);
}
if (transportTypePanelControl != null) {
transportTypePanelControl.showConfigurationPanel(transportType);
IWizardConfigurationPanel panel = transportTypePanelControl.getConfigurationPanel(transportType);
if (panel instanceof IDataExchangeNode) {
((IDataExchangeNode) panel).setupData(data);
}
}
}
}
// Mark the control update as completed now
setIsUpdating(false);
// Re-evaluate the dirty state
dataChanged(null);
// Adjust the control enablement
updateEnablement();
}
/**
* Initialize the page widgets based of the data from the given peer node.
* <p>
* This method may called multiple times during the lifetime of the page and the given
* configuration node might be even <code>null</code>.
*
* @param node The peer node or <code>null</code>.
*/
public void setupData(final IPeerNode node) {
// If the section is dirty, nothing is changed
if (isDirty()) return;
boolean updateWidgets = true;
// If the passed in node is the same as the previous one,
// no need for updating the section widgets.
if ((node == null && od == null) || (node != null && node.equals(od))) {
updateWidgets = false;
}
// Besides the node itself, we need to look at the node data to determine
// if the widgets needs to be updated. For the comparisation, keep the
// current properties of the original data copy in a temporary container.
final IPropertiesContainer previousOdc = new PropertiesContainer();
previousOdc.setProperties(odc.getProperties());
// Store a reference to the original data
od = node;
// Clean the original data copy
odc.clearProperties();
// Clean the working copy
wc.clearProperties();
// If no data is available, we are done
if (node == null) return;
// Thread access to the model is limited to the executors thread.
// Copy the data over to the working copy to ease the access.
Protocol.invokeAndWait(new Runnable() {
@Override
public void run() {
// The section is handling the transport name and the
// transport type specific properties. Ignore other properties.
odc.setProperty(IPeer.ATTR_TRANSPORT_NAME, node.getPeer().getTransportName());
if (transportTypePanelControl != null) {
IPropertiesContainer src = new PropertiesContainer();
Map<String, String> properties = node.getPeer().getAttributes();
for (Entry<String, String> entry : properties.entrySet()) {
src.setProperty(entry.getKey(), entry.getValue());
}
for (String id : transportTypePanelControl.getConfigurationPanelIds()) {
IWizardConfigurationPanel panel = transportTypePanelControl.getConfigurationPanel(id);
if (panel instanceof IDataExchangeNode3) {
((IDataExchangeNode3) panel).copyData(src, odc);
}
}
}
// Initially, the working copy is a duplicate of the original data copy
wc.setProperties(odc.getProperties());
}
});
// From here on, work with the working copy only!
// If the original data copy does not match the previous original
// data copy, the widgets needs to be updated to present the correct data.
if (!previousOdc.getProperties().equals(odc.getProperties())) {
updateWidgets = true;
}
if (updateWidgets) {
setupData(wc);
}
else {
// Re-evaluate the dirty state
dataChanged(null);
// Adjust the control enablement
updateEnablement();
}
}
/* (non-Javadoc)
* @see org.eclipse.tcf.te.ui.interfaces.data.IDataExchangeNode#extractData(org.eclipse.tcf.te.runtime.interfaces.properties.IPropertiesContainer)
*/
@Override
public void extractData(IPropertiesContainer data) {
Assert.isNotNull(data);
if (transportTypePanelControl != null) {
String[] ids = transportTypePanelControl.getConfigurationPanelIds();
for (String id : ids) {
IWizardConfigurationPanel panel = transportTypePanelControl.getConfigurationPanel(id);
if (panel instanceof IDataExchangeNode) {
if (panel instanceof IDataExchangeNode3) {
((IDataExchangeNode3) panel).removeData(data);
}
}
}
IWizardConfigurationPanel panel = transportTypePanelControl.getActiveConfigurationPanel();
if (panel instanceof IDataExchangeNode) {
((IDataExchangeNode) panel).extractData(data);
}
}
if (transportTypeControl != null) {
data.setProperty(IPeer.ATTR_TRANSPORT_NAME, transportTypeControl.getSelectedTransportType());
}
}
/**
* Stores the page widgets current values to the given peer node.
* <p>
* This method may called multiple times during the lifetime of the page and the given peer node
* might be even <code>null</code>.
*
* @param node The GDB Remote configuration node or <code>null</code>.
*/
public void extractData(final IPeerNode node) {
// If no data is available, we are done
if (node == null) {
return;
}
// The list of removed attributes
final List<String> removed = new ArrayList<String>();
// Get the current key set from the working copy
Set<String> currentKeySet = wc.getProperties().keySet();
extractData(wc);
// If the data has not changed compared to the original data copy,
// we are done here and return immediately
if (odc.equals(wc)) {
return;
}
// Get the new key set from the working copy
Set<String> newKeySet = wc.getProperties().keySet();
// Everything from the old key set not found in the new key set is a removed attribute
for (String key : currentKeySet) {
if (!newKeySet.contains(key)) {
removed.add(key);
}
}
// Copy the working copy data back to the original properties container
Protocol.invokeAndWait(new Runnable() {
@Override
public void run() {
// To update the peer attributes, the peer needs to be recreated
IPeer oldPeer = node.getPeer();
// Create a write able copy of the peer attributes
Map<String, String> attributes = new HashMap<String, String>(oldPeer
.getAttributes());
// Clean out the removed attributes
for (String key : removed) {
attributes.remove(key);
}
// Update with the current configured attributes
for (String key : wc.getProperties().keySet()) {
String value = wc.getStringProperty(key);
if (value != null) {
attributes.put(key, value);
}
else {
attributes.remove(key);
}
}
// If there is still a open channel to the old peer, close it by force
IChannel channel = Tcf.getChannelManager().getChannel(oldPeer);
if (channel != null) {
channel.close();
}
// Create the new peer
IPeer newPeer = new Peer(attributes);
// Update the peer node instance (silently)
boolean changed = node.setChangeEventsEnabled(false);
node.setProperty(IPeerNodeProperties.PROPERTY_INSTANCE, newPeer);
// As the transport changed, we have to reset the state back to "unknown"
// and clear out the services and DNS markers
node.setProperty("dns.name.transient", null); //$NON-NLS-1$
node.setProperty("dns.lastIP.transient", null); //$NON-NLS-1$
node.setProperty("dns.skip.transient", null); //$NON-NLS-1$
if (changed) {
node.setChangeEventsEnabled(true);
}
}
});
}
/*
* (non-Javadoc)
* @see org.eclipse.tcf.te.ui.forms.parts.AbstractSection#isValid()
*/
@Override
public boolean isValid() {
// Validation is skipped while the controls are updated
if (isUpdating()) return true;
boolean valid = super.isValid();
if (transportTypeControl != null) {
valid &= transportTypeControl.isValid();
if (transportTypeControl.getMessageType() > getMessageType()) {
setMessage(transportTypeControl.getMessage(), transportTypeControl.getMessageType());
}
}
if (transportTypePanelControl != null) {
valid &= transportTypePanelControl.isValid();
if (transportTypePanelControl.getMessageType() > getMessageType()) {
setMessage(transportTypePanelControl.getMessage(), transportTypePanelControl.getMessageType());
}
}
return valid;
}
/*
* (non-Javadoc)
* @see org.eclipse.ui.forms.AbstractFormPart#commit(boolean)
*/
@Override
public void commit(boolean onSave) {
// Remember the current dirty state
boolean needsSaving = isDirty();
// Call the super implementation (resets the dirty state)
super.commit(onSave);
// Nothing to do if not on save or saving is not needed
if (!onSave || !needsSaving) {
return;
}
// Extract the data into the original data node
extractData(od);
}
/**
* Called to signal that the data associated has been changed.
*
* @param e The event which triggered the invocation or <code>null</code>.
*/
public void dataChanged(TypedEvent e) {
// dataChanged is not evaluated while the controls are updated
if (isUpdating()) return;
boolean isDirty = false;
if (transportTypeControl != null) {
String transportType = transportTypeControl.getSelectedTransportType();
if (!ITransportTypes.TRANSPORT_TYPE_CUSTOM.equals(transportType)) {
if ("".equals(transportType)) { //$NON-NLS-1$
String value = odc.getStringProperty(IPeer.ATTR_TRANSPORT_NAME);
isDirty |= value != null && !"".equals(value.trim()); //$NON-NLS-1$
}
else {
isDirty |= !odc.isProperty(IPeer.ATTR_TRANSPORT_NAME, transportType);
}
}
if (transportTypePanelControl != null) {
isDirty |= transportTypePanelControl.getConfigurationPanel(transportType).dataChanged(odc, e);
}
}
// If dirty, mark the form part dirty.
// Otherwise call refresh() to reset the dirty (and stale) flag
markDirty(isDirty);
// Adjust the control enablement
updateEnablement();
}
/**
* Updates the given set of attributes with the current values of the page widgets.
*
* @param attributes The attributes to update. Must not be <code>null</code>:
*/
public void updateAttributes(IPropertiesContainer attributes) {
Assert.isNotNull(attributes);
if (transportTypePanelControl != null) {
String[] ids = transportTypePanelControl.getConfigurationPanelIds();
for (String id : ids) {
IWizardConfigurationPanel panel = transportTypePanelControl.getConfigurationPanel(id);
if (panel instanceof IDataExchangeNode) {
if (panel instanceof IDataExchangeNode3) {
((IDataExchangeNode3) panel).removeData(attributes);
}
}
}
IWizardConfigurationPanel panel = transportTypePanelControl.getActiveConfigurationPanel();
if (panel instanceof IDataExchangeNode) {
((IDataExchangeNode) panel).extractData(attributes);
}
}
if (transportTypeControl != null) {
attributes.setProperty(IPeer.ATTR_TRANSPORT_NAME, transportTypeControl.getSelectedTransportType());
}
}
/**
* Updates the control enablement.
*/
protected void updateEnablement() {
// Determine the input
final Object input = od; // getManagedForm().getInput();
// The transport type control is enabled for static peers
if (transportTypeControl != null) {
boolean enabled = !isReadOnly() && (!(input instanceof IPeerNode) || ((IPeerNode)input).getConnectState() == IConnectable.STATE_DISCONNECTED);
SWTControlUtil.setEnabled(transportTypeControl.getEditFieldControl(), enabled && SWTControlUtil.getItemCount(transportTypeControl.getEditFieldControl()) > 1);
if (transportTypePanelControl != null) {
transportTypePanelControl.getConfigurationPanel(transportTypeControl.getSelectedTransportType()).setEnabled(enabled);
}
}
}
/* (non-Javadoc)
* @see org.eclipse.tcf.te.ui.forms.parts.AbstractSection#saveWidgetValues(org.eclipse.jface.dialogs.IDialogSettings)
*/
@Override
public void saveWidgetValues(IDialogSettings settings) {
super.saveWidgetValues(settings);
if (settings != null) {
if (transportTypePanelControl != null) {
transportTypePanelControl.saveWidgetValues(settings, TransportSection.class.getSimpleName());
}
}
}
/* (non-Javadoc)
* @see org.eclipse.tcf.te.ui.forms.parts.AbstractSection#restoreWidgetValues(org.eclipse.jface.dialogs.IDialogSettings)
*/
@Override
public void restoreWidgetValues(IDialogSettings settings) {
super.restoreWidgetValues(settings);
if (settings != null) {
if (transportTypePanelControl != null) {
transportTypePanelControl.restoreWidgetValues(settings, TransportSection.class.getSimpleName());
}
}
}
}