/******************************************************************************* * 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.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jface.dialogs.IMessageProvider; import org.eclipse.osgi.util.NLS; 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.persistence.interfaces.IPersistableNodeProperties; import org.eclipse.tcf.te.runtime.persistence.interfaces.IURIPersistenceService; import org.eclipse.tcf.te.runtime.properties.PropertiesContainer; import org.eclipse.tcf.te.runtime.services.ServiceManager; import org.eclipse.tcf.te.runtime.statushandler.StatusHandlerUtil; import org.eclipse.tcf.te.runtime.utils.StatusHelper; 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.locator.model.ModelManager; import org.eclipse.tcf.te.tcf.ui.activator.UIPlugin; import org.eclipse.tcf.te.tcf.ui.editor.controls.InfoSectionPeerNameControl; import org.eclipse.tcf.te.tcf.ui.help.IContextHelpIds; 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 general information section implementation. */ public class GeneralInformationSection extends AbstractSection { // The section sub controls private InfoSectionPeerNameControl nameControl = null; // 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 existing configuration names. Used to generate a unique name // and validate the wizard /* default */ final java.util.List<String> usedNames = new ArrayList<String>(); /** * 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 GeneralInformationSection(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() { if (nameControl != null) { nameControl.dispose(); nameControl = null; } super.dispose(); } /* (non-Javadoc) * @see org.eclipse.tcf.te.ui.forms.parts.AbstractSection#getAdapter(java.lang.Class) */ @Override public Object getAdapter(Class adapter) { if (InfoSectionPeerNameControl.class.equals(adapter)) { return nameControl; } 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.GeneralInformationSection_title); section.setDescription(Messages.GeneralInformationSection_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 peer name control nameControl = new InfoSectionPeerNameControl(this) { @Override public boolean isValid() { boolean valid = true; String name = getEditFieldControlTextForValidation(); if (!"".equals(name)) { //$NON-NLS-1$ // Name is not empty -> check against the list of used names valid = !infoSection.usedNames.contains(name.trim().toUpperCase()); if (!valid) { setMessage(Messages.GeneralInformationSection_error_nameInUse, IMessageProvider.ERROR); } } if (!valid && getControlDecoration() != null) { // Setup and show the control decoration if necessary if (isEnabled()) { // Update the control decorator updateControlDecoration(getMessage(), getMessageType()); } } return valid ? super.isValid() : false; } }; nameControl.setFormToolkit(toolkit); nameControl.setParentControlIsInnerPanel(true); nameControl.setupPanel(client); // 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 peer 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 ID, the name and // the link state. Ignore other properties. odc.setProperty(IPeer.ATTR_ID, node.getPeer().getAttributes().get(IPeer.ATTR_ID)); odc.setProperty(IPeer.ATTR_NAME, node.getPeer().getAttributes().get(IPeer.ATTR_NAME)); // 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) { // Mark the control update as in-progress now setIsUpdating(true); if (nameControl != null) { nameControl.setEditFieldControlText(wc.getStringProperty(IPeer.ATTR_NAME)); } // Mark the control update as completed now setIsUpdating(false); } initializeUsedNameList(); // 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 peer 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 widget data into the working copy if (nameControl != null) { String name = nameControl.getEditFieldControlText(); boolean used = name != null && usedNames.contains(name.trim().toUpperCase()); if (!used && name != null && !"".equals(name.trim()) ) { //$NON-NLS-1$ wc.setProperty(IPeer.ATTR_NAME, name); } else { // Build up the message template String template = NLS.bind(Messages.OverviewEditorPage_error_save, wc.getStringProperty(IPeer.ATTR_NAME), Messages.PossibleCause); // Handle the status Status status = new Status(IStatus.ERROR, UIPlugin.getUniqueIdentifier(), used ? Messages.GeneralInformationSection_error_nameInUse : Messages.GeneralInformationSection_error_emptyName); StatusHandlerUtil.handleStatus(status, od, template, null, IContextHelpIds.MESSAGE_SAVE_FAILED, GeneralInformationSection.this, null); } } // If the peer name changed, copy the working copy data back to // the original properties container if (!odc.getStringProperty(IPeer.ATTR_NAME).equals(wc.getStringProperty(IPeer.ATTR_NAME))) { 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 the (managed) attributes from the working copy attributes.put(IPeer.ATTR_NAME, wc.getStringProperty(IPeer.ATTR_NAME)); // Remove the persistence storage URI (if set) attributes.remove(IPersistableNodeProperties.PROPERTY_URI); // 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.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 (nameControl != null) { valid &= nameControl.isValid(); setMessage(nameControl.getMessage(), nameControl.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; } // Remember the old name String oldName = odc.getStringProperty(IPeer.ATTR_NAME); // Extract the data into the original data node extractData(od); // If the name changed, trigger a delete of the old data if (!oldName.equals(wc.getStringProperty(IPeer.ATTR_NAME))) { try { // Get the persistence service IURIPersistenceService uRIPersistenceService = ServiceManager.getInstance().getService(IURIPersistenceService.class); if (uRIPersistenceService == null) { throw new IOException("Persistence service instance unavailable."); //$NON-NLS-1$ } // Remove the old persistence storage using the original data copy Map<String,String> oldData = new HashMap<String, String>(); for (String key : odc.getProperties().keySet()) { oldData.put(key, odc.getStringProperty(key)); } uRIPersistenceService.delete(new Peer(oldData), null); } catch (IOException e) { // Build up the message template String template = NLS.bind(Messages.GeneralInformationSection_error_delete, oldName, Messages.PossibleCause); // Handle the status StatusHandlerUtil.handleStatus(StatusHelper.getStatus(e), od, template, null, IContextHelpIds.MESSAGE_DELETE_FAILED, GeneralInformationSection.this, null); } } } /** * 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; // Compare the data if (nameControl != null) { String name = nameControl.getEditFieldControlText(); if ("".equals(name)) { //$NON-NLS-1$ String value = odc.getStringProperty(IPeer.ATTR_NAME); isDirty |= value != null && !"".equals(value.trim()); //$NON-NLS-1$ } else { isDirty |= !odc.isProperty(IPeer.ATTR_NAME, name); } } // 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() { // Determine the input final Object input = getManagedForm().getInput(); if (input instanceof IPeerNode) { SWTControlUtil.setEnabled(nameControl.getEditFieldControl(), ((IPeerNode)input).getConnectState() == IConnectable.STATE_DISCONNECTED); } } /** * Initialize the used name list. */ protected void initializeUsedNameList() { usedNames.clear(); Runnable runnable = new Runnable() { @Override public void run() { // Get all peer model objects IPeerNode[] peers = ModelManager.getPeerModel().getPeerNodes(); // Loop them and find the ones which are of our handled types for (IPeerNode peerNode : peers) { if (!peerNode.equals(od)) { String name = peerNode.getPeer().getName(); Assert.isNotNull(name); if (!"".equals(name) && !usedNames.contains(name)) { //$NON-NLS-1$ usedNames.add(name.trim().toUpperCase()); } } } } }; Assert.isTrue(!Protocol.isDispatchThread()); Protocol.invokeAndWait(runnable); } }