/******************************************************************************* * 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 org.eclipse.core.runtime.Assert; 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.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.interfaces.IPeerProperties; 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.PeerAttributesTablePart; import org.eclipse.tcf.te.tcf.ui.nls.Messages; import org.eclipse.tcf.te.ui.forms.parts.AbstractSection; 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 custom attributes section implementation. */ public class AttributesSection extends AbstractSection { // The section sub controls private PeerAttributesTablePart tablePart; // Reference to the original data object /* default */ 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(); /* * The list of banned names for the table parts. Banned names are names the user is not allowed * to add to the table. */ private static final String[] BANNED_NAMES = new String[] { IPeer.ATTR_ID, IPeer.ATTR_AGENT_ID, IPeer.ATTR_SERVICE_MANGER_ID, "ServerManagerID", //$NON-NLS-1$ IPeer.ATTR_NAME, IPeer.ATTR_TRANSPORT_NAME, IPeer.ATTR_IP_HOST, IPeer.ATTR_IP_PORT, "PipeName", //$NON-NLS-1$ "redirect.proxy", //$NON-NLS-1$ IPeerProperties.PROP_VISIBLE, "ClientID" //$NON-NLS-1$ }; /* * The list of filtered attributes. This attributes are filtered from the data given to the * table part on initialization. The attributes listed here are handled by other controls of the * overview page. */ private static final String[] FILTERED_NAMES = new String [] { IPeer.ATTR_ID, IPeer.ATTR_AGENT_ID, IPeer.ATTR_SERVICE_MANGER_ID, "ServerManagerID", //$NON-NLS-1$ IPeer.ATTR_NAME, IPeer.ATTR_TRANSPORT_NAME, IPeer.ATTR_IP_HOST, IPeer.ATTR_IP_PORT, "PipeName", //$NON-NLS-1$ "redirect.proxy", //$NON-NLS-1$ IPeerProperties.PROP_VISIBLE, "ClientID" //$NON-NLS-1$ }; /** * 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 AttributesSection(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 (tablePart != null) { tablePart.dispose(); tablePart = null; } } /* (non-Javadoc) * @see org.eclipse.tcf.te.ui.forms.parts.AbstractSection#getAdapter(java.lang.Class) */ @Override public Object getAdapter(Class adapter) { if (PeerAttributesTablePart.class.equals(adapter)) { return tablePart; } 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.AttributesSection_title); section.setDescription(Messages.AttributesSection_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); tablePart = new PeerAttributesTablePart() { @Override protected void onTableModified() { dataChanged(null); } }; tablePart.setMinSize(SWTControlUtil.convertWidthInCharsToPixels(client, 20), SWTControlUtil.convertHeightInCharsToPixels(client, 6)); tablePart.setBannedNames(BANNED_NAMES); tablePart.createControl(client, SWT.SINGLE | SWT.FULL_SELECTION, 2, toolkit); // Adjust the control enablement updateEnablement(); // Mark the control update as completed now setIsUpdating(false); } /** * 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); } } /** * 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() { Map<String, String> properties = node.getPeer().getAttributes(); for (Entry<String, String> entry : properties.entrySet()) { wc.setProperty(entry.getKey(), entry.getValue()); odc.setProperty(entry.getKey(), entry.getValue()); } } }); // From here on, work with the working copy only! // Remove the filtered attributes from the original data and working copy in // order to get an exact base to determine the dirty state of the section. for (String filteredName : FILTERED_NAMES) { wc.setProperty(filteredName, null); odc.setProperty(filteredName, null); } // Remove all "*.transient" properties for (String name : odc.getProperties().keySet()) { if (name.endsWith(".silent") || name.endsWith(".transient")) { //$NON-NLS-1$ //$NON-NLS-2$ wc.setProperty(name, null); odc.setProperty(name, null); } } // 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) { // Mark the control update as in-progress now setIsUpdating(true); // Make a <String, String> map out of the remaining properties Map<String, String> attributes = new HashMap<String, String>(); Map<String, Object> properties = wc.getProperties(); for (Entry<String, Object> entry : properties.entrySet()) { attributes.put(entry.getKey(), entry.getValue().toString()); } // Pass on to the table part if (tablePart != null) { tablePart.setAttributes(attributes); } // Mark the control update as completed now setIsUpdating(false); } // Re-evaluate the dirty state dataChanged(null); // Adjust the control enablement updateEnablement(); } /** * 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; } // Extract the table part attributes into the working copy // Properties not longer available in the table part attributes // are removed. Map<String, String> attributes = tablePart.getAttributes(); Map<String, Object> properties = wc.getProperties(); final List<String> removed = new ArrayList<String>(); for (String key : properties.keySet()) { if (attributes.containsKey(key)) { wc.setProperty(key, attributes.get(key)); attributes.remove(key); } else { wc.setProperty(key, null); removed.add(key); } } // Add all remaining (new) attributes if (!attributes.isEmpty()) { for (String key : attributes.keySet()) { wc.setProperty(key, attributes.get(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()); // 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); } } // Remove the disappeared properties for (String key : removed) { attributes.remove(key); } // 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); if (changed) { node.setChangeEventsEnabled(true); } } }); } /* (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 (tablePart != null) { // Get the attributes from the table part Map<String, String> attributes = tablePart.getAttributes(); Map<String, Object> properties = odc.getProperties(); if (attributes.size() != properties.size()) { isDirty = true; } else { for (Entry<String, String> entry : attributes.entrySet()) { if (!properties.containsKey(entry.getKey()) || !properties.get(entry.getKey()).equals(entry.getValue())) { isDirty = true; break; } } } } // If dirty, mark the form part dirty. // Otherwise call refresh() to reset the dirty (and stale) flag markDirty(isDirty); } /** * Updates the control enablement. */ protected void updateEnablement() { if (tablePart != null) { // Determine the input final Object input = getManagedForm().getInput(); if (input instanceof IPeerNode) { tablePart.setReadOnly(((IPeerNode)input).getConnectState() != IConnectable.STATE_DISCONNECTED); } } } }