/*******************************************************************************
* This file is protected by Copyright.
* Please refer to the COPYRIGHT file distributed with this source distribution.
*
* This file is part of REDHAWK IDE.
*
* 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
*******************************************************************************/
package gov.redhawk.ide.spd.internal.ui.handlers;
import gov.redhawk.ide.spd.internal.ui.editor.ComponentEditor;
import gov.redhawk.model.sca.util.ModelUtil;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import mil.jpeojtrs.sca.scd.AbstractPort;
import mil.jpeojtrs.sca.scd.InheritsInterface;
import mil.jpeojtrs.sca.scd.Interface;
import mil.jpeojtrs.sca.scd.Ports;
import mil.jpeojtrs.sca.scd.Provides;
import mil.jpeojtrs.sca.scd.ScdPackage;
import mil.jpeojtrs.sca.scd.SupportsInterface;
import mil.jpeojtrs.sca.scd.Uses;
import mil.jpeojtrs.sca.spd.SoftPkg;
import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.emf.common.command.Command;
import org.eclipse.emf.common.command.CompoundCommand;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.util.FeatureMap;
import org.eclipse.emf.edit.command.RemoveCommand;
import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.ui.handlers.HandlerUtil;
/**
* An implementation of {@link AbstractHandler} for removing ports from a SoftwareComponent.
* @since 6.0
*/
public class RemovePortsHandler extends AbstractHandler {
private EditingDomain editingDomain;
private Resource resource;
private SoftPkg softPkg;
private Map<String, Interface> interfaceMap;
/**
* Default Constructor for instantiation by framework.
*/
public RemovePortsHandler() {
// DefaultConstructor
}
/**
* Constructor for use within the package so this handler can be used by other handlers.
* @param editor
*
* @param editingDomain
* @param resource
* @param softPkg
*/
public RemovePortsHandler(final EditingDomain editingDomain, final Resource resource, final SoftPkg softPkg) {
this.editingDomain = editingDomain;
this.resource = resource;
this.softPkg = softPkg;
this.interfaceMap = PortsHandlerUtil.getInterfaceMap(this.softPkg);
}
/**
* {@inheritDoc}
*/
@Override
public Object execute(final ExecutionEvent event) throws ExecutionException {
final ISelection selection = HandlerUtil.getActiveWorkbenchWindow(event).getSelectionService().getSelection();
final ComponentEditor editor = (ComponentEditor) HandlerUtil.getActiveEditor(event);
this.editingDomain = editor.getEditingDomain();
this.resource = editor.getMainResource();
this.softPkg = ModelUtil.getSoftPkg(this.resource);
this.interfaceMap = PortsHandlerUtil.getInterfaceMap(this.softPkg);
final List<Object> ports = Arrays.asList(((IStructuredSelection) selection).toArray());
Command command = this.createRemovePortCommand(ports, new HashSet<String>());
this.editingDomain.getCommandStack().execute(command);
return null;
}
/**
* Creates and returns a command to remove the specified ports.
*
* @param ports the ports to remove from the {@link mil.jpeojtrs.sca.scd.SoftwareComponent}
* @param ignoreIds ignore the specified repIds when considering which interfaces to remove
* @return the {@link Command} to remove the port and all associated {@link Interface}
*/
public Command createRemovePortCommand(final Collection<Object> ports, final Set<String> ignoreIds) {
final Set<String> repIds = new HashSet<String>();
final List<EObject> removePorts = new ArrayList<EObject>();
final CompoundCommand command = new CompoundCommand("Remove Ports");
for (final Object obj : ports) {
final EObject port = (EObject) AdapterFactoryEditingDomain.unwrap(obj);
if (port instanceof AbstractPort) {
for (AbstractPort p : getPorts().getAllPorts()) {
if (p.equals(port)) {
final AbstractPort sibling = p.getSibling();
if (sibling == null || !(p instanceof Uses)) {
repIds.add(p.getRepID());
command.append(RemoveCommand.create(this.editingDomain, getPorts(), p.eContainingFeature(), p));
if (sibling != null) {
command.append(RemoveCommand.create(this.editingDomain, getPorts(), sibling.eContainingFeature(), sibling));
}
removePorts.add(p);
}
}
}
}
}
for (final String repId : repIds) {
if (this.interfaceMap.containsKey(repId) && !ignoreIds.contains(repId)) {
final Command interfaceCommand = this.createRemoveInterfaceCommand(removePorts, ignoreIds, this.interfaceMap.get(repId));
if (interfaceCommand != null) {
command.append(interfaceCommand);
this.interfaceMap.remove(repId);
}
}
}
return command;
}
/**
* Creates and returns a command to remove the interface and associated inherited interfaces if they are not
* referenced by other ports.
*
* @param i the {@link Interface} to remove
* @param removePorts the {@link Uses} or {@link Provides} to ignore when considering which interfaces to remove
* @param removeInterfaces the {@link Set} of repIds already scheduled for removal
* @return a {@link Command} to remove the specified {@link Interface} and {@link InheritsInterface} if they can be
* removed
*/
public Command createRemoveInterfaceCommand(final Collection<EObject> removePorts, final Set<String> removeInterfaces, final Interface i) {
if (!containsRepId(i.getRepid(), removePorts)) {
final CompoundCommand command = new CompoundCommand("Remove Interfaces");
for (final InheritsInterface inherited : i.getInheritsInterfaces()) {
if (!containsRepId(inherited.getRepid(), removePorts)) {
if (!removeInterfaces.contains(inherited.getRepid())) {
// If the inherited interface isn't referenced by another port and isn't already scheduled for
// removal, make a recursive call to remove it
if (inherited.getInterface() != null) {
command.append(this.createRemoveInterfaceCommand(removePorts, removeInterfaces, inherited.getInterface()));
}
}
}
}
// If the interface isn't already scheduled for removal, create a command to remove it
if (removeInterfaces.add(i.getRepid())) {
command.append(RemoveCommand.create(this.editingDomain, PortsHandlerUtil.getInterfaces(this.softPkg),
ScdPackage.Literals.INTERFACES__INTERFACE, i));
}
return command;
}
return null;
}
/**
* Searches the software component for the specified repID.
*
* @param repID the repID to search for
* @param removePorts the {@link Uses} or {@link Provides} ports being removed, these are disregarded
* @param repIds the {@link Set} of repIds belonging to the {@link mil.jpeojtrs.sca.scd.SoftwareComponent}
* @return <code> true </code> if the repID is found; <code> false </code> otherwise
*/
private boolean containsRepId(final String repID, final Collection<EObject> removePorts) {
// If it's a supported interface, return true;
if (getSupportsInterfaceIds().contains(repID)) {
return true;
} else {
// Go through the ports and try to match
for (final FeatureMap.Entry entry : getPorts().getGroup()) {
if (!(entry.getValue() instanceof AbstractPort)) {
continue; // unexpected
}
final AbstractPort port = (AbstractPort) entry.getValue();
// Ignore ports being removed
if (!removePorts.contains(port) && !removePorts.contains(port.getSibling())) {
if (port instanceof Provides) {
if (!repID.equals(((Provides) port).getRepID())) {
for (final InheritsInterface inherits : ((Provides) port).getInterface().getInheritsInterfaces()) {
if (repID.equals(inherits.getRepid())) {
return true;
}
}
} else {
return true;
}
} else { // Uses
if (!repID.equals(((Uses) port).getRepID())) {
for (final InheritsInterface inherits : ((Uses) port).getInterface().getInheritsInterfaces()) {
if (repID.equals(inherits.getRepid())) {
return true;
}
}
} else {
return true;
}
}
}
}
}
return false;
}
/**
* Gets the ports via the {@link SoftPkg}
*
* @return the {@link Ports} associated with the {@link mil.jpeojtrs.sca.scd.SoftwareComponent}
*/
private Ports getPorts() {
return this.softPkg.getDescriptor().getComponent().getComponentFeatures().getPorts();
}
/**
* Gets a set of {@link SupportsInterface} repIds.
*
* @return the set of repIds supported by the SoftwareComponent
*/
private Set<String> getSupportsInterfaceIds() {
final Set<String> ids = new HashSet<String>();
for (final SupportsInterface supports : this.softPkg.getDescriptor().getComponent().getComponentFeatures().getSupportsInterface()) {
ids.add(supports.getRepId());
}
return ids;
}
}