/**
* 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.diagram.util;
import gov.redhawk.diagram.activator.PluginActivator;
import gov.redhawk.eclipsecorba.idl.Identifiable;
import gov.redhawk.eclipsecorba.idl.IdlInterfaceDcl;
import gov.redhawk.eclipsecorba.library.IdlLibrary;
import gov.redhawk.ui.RedhawkUiActivator;
import gov.redhawk.ui.editor.IIdlLibraryService;
import java.util.List;
import mil.jpeojtrs.sca.partitioning.ComponentInstantiation;
import mil.jpeojtrs.sca.partitioning.ComponentSupportedInterfaceStub;
import mil.jpeojtrs.sca.partitioning.FindByStub;
import mil.jpeojtrs.sca.partitioning.PartitioningPackage;
import mil.jpeojtrs.sca.partitioning.ProvidesPortStub;
import mil.jpeojtrs.sca.partitioning.UsesDeviceStub;
import mil.jpeojtrs.sca.partitioning.UsesPortStub;
import mil.jpeojtrs.sca.sad.Port;
import mil.jpeojtrs.sca.scd.Interface;
import mil.jpeojtrs.sca.scd.ScdPackage;
import mil.jpeojtrs.sca.scd.SupportsInterface;
import mil.jpeojtrs.sca.spd.SpdPackage;
import mil.jpeojtrs.sca.util.ScaEcoreUtils;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.ui.statushandlers.StatusManager;
/**
* Utilities for determining if a uses port is compatible (or even a suggested match) for a provides port / component
* supported interface.
*/
public class InterfacesUtil {
private InterfacesUtil() {
}
/**
* Determine whether or not a uses port is a suggested match for a provides port or component supported interface.
* @param source A {@link UsesPortStub}
* @param target A {@link ProvidesPortStub} or {@link ComponentSupportedInterfaceStub}
* @return Return a boolean that shows whether or not the ports are a suggested match
* @since 7.0
*/
public static boolean areSuggestedMatch(final UsesPortStub source, final EObject target) {
// They must be compatible ports or we won't suggest a match
if (!areCompatible(source, target)) {
return false;
}
// If we're matching port -> port, and either of the ports is on a uses device, use name matching to determine
// if the ports are be a suggested match
if (target instanceof ProvidesPortStub && (source.eContainer() instanceof UsesDeviceStub || target.eContainer() instanceof UsesDeviceStub)) {
final ProvidesPortStub providesStub = (ProvidesPortStub) target;
// Take the beginning of each name before any underscore
String usesPortName = source.getName();
int index = usesPortName.indexOf('_');
if (index != -1) {
usesPortName = usesPortName.substring(0, index);
}
String providesPortName = providesStub.getName();
index = providesPortName.indexOf('_');
if (index != -1) {
providesPortName = providesPortName.substring(0, index);
}
// If the beginnings of the names are equal, we'll suggest this as a match
return usesPortName.equals(providesPortName);
} else if (target instanceof ComponentSupportedInterfaceStub && target.eContainer() instanceof FindByStub) {
// Don't suggest connections to FindBys with unbounded IDL types, which can match anything
return FindByStubUtil.getFindByStubRepId((FindByStub) target.eContainer()) != null;
}
// We know the source and target are compatible, so suggest the match
return true;
}
/**
* Determine whether or not a uses port is compatible to connect to a provides port or a component supported
* interface. The port/supported interface can belong to a component, usesdevice, or findby.
* @param source A {@link UsesPortStub}
* @param target A {@link ProvidesPortStub} or {@link ComponentSupportedInterfaceStub}
* @return True if the objects are compatible for connection
* @since 7.0
*/
public static boolean areCompatible(final UsesPortStub source, final EObject target) {
if (source == null || target == null) {
return false;
}
EObject sourceContainer = source.eContainer();
if (sourceContainer instanceof ComponentInstantiation) {
return areCompatibleComponentToTarget(source, target);
} else if (sourceContainer instanceof UsesDeviceStub) {
return areCompatibleUsesDeviceToTarget(source, target);
} else if (sourceContainer instanceof FindByStub) {
return areCompatibleFindByToTarget(source, target);
} else {
String sourceContainerType = (sourceContainer != null) ? sourceContainer.getClass().getCanonicalName() : "null";
throw new IllegalArgumentException("Invalid source container type: " + sourceContainerType);
}
}
/**
* Used by {@link #areCompatible(UsesPortStub, EObject)} to determine if a component's uses port is compatible
* with a target.
* @param source
* @param target
* @return
*/
private static boolean areCompatibleComponentToTarget(final UsesPortStub source, final EObject target) {
EObject targetContainer = target.eContainer();
if (target instanceof ProvidesPortStub) {
if (targetContainer instanceof ComponentInstantiation) {
return areComponentToComponentCompatible(source, target);
} else if (targetContainer instanceof FindByStub) {
return true;
} else if (targetContainer instanceof UsesDeviceStub) {
return true;
} else {
String targetContainerType = (targetContainer != null) ? targetContainer.getClass().getCanonicalName() : "null";
throw new IllegalArgumentException("Invalid target container type: " + targetContainerType);
}
} else if (target instanceof ComponentSupportedInterfaceStub) {
if (targetContainer instanceof ComponentInstantiation) {
return areComponentToComponentCompatible(source, target);
} else if (targetContainer instanceof FindByStub) {
return areComponentToFindByComponentSupportedInterfaceStubCompatible(source, (ComponentSupportedInterfaceStub) target);
} else if (targetContainer instanceof UsesDeviceStub) {
return areComponentToUsesDeviceComponentSupportedInterfaceCompatible(source, (ComponentSupportedInterfaceStub) target);
} else {
String targetContainerType = (targetContainer != null) ? targetContainer.getClass().getCanonicalName() : "null";
throw new IllegalArgumentException("Invalid target container type: " + targetContainerType);
}
} else if (target instanceof Port) {
// The target business object may be a Port if the case of an external port, but these can be ignored
return false;
} else {
throw new IllegalArgumentException("Invalid target type: " + target.getClass().getCanonicalName());
}
}
/**
* Used by {@link #areCompatible(UsesPortStub, EObject)} to determine if a uses device's uses port is compatible
* with a target.
* @param source
* @param target
* @return
*/
private static boolean areCompatibleUsesDeviceToTarget(final UsesPortStub source, final EObject target) {
// For a uses port on a uses device, we have no type info for the port
EObject targetContainer = target.eContainer();
if (target instanceof ProvidesPortStub) {
if (targetContainer instanceof ComponentInstantiation) {
return true;
} else if (targetContainer instanceof FindByStub) {
// TODO: Is this true? usesdevice port -> findby port
return true;
} else if (targetContainer instanceof UsesDeviceStub) {
return true;
} else {
String targetContainerType = (targetContainer != null) ? targetContainer.getClass().getCanonicalName() : "null";
throw new IllegalArgumentException("Invalid target container type: " + targetContainerType);
}
} else if (target instanceof ComponentSupportedInterfaceStub) {
if (targetContainer instanceof ComponentInstantiation) {
return true;
} else if (targetContainer instanceof FindByStub) {
// uses_device:port -> find_by:comp_supported_intf
// This doesn't make any sense,
return true;
} else if (targetContainer instanceof UsesDeviceStub) {
// TODO: Is this true? usesdevice port -> usesdevice comp. supported intf.
return true;
} else {
String targetContainerType = (targetContainer != null) ? targetContainer.getClass().getCanonicalName() : "null";
throw new IllegalArgumentException("Invalid target container type: " + targetContainerType);
}
} else {
throw new IllegalArgumentException("Invalid target type: " + target.getClass().getCanonicalName());
}
}
/**
* Used by {@link #areCompatible(UsesPortStub, EObject)} to determine if a find by's uses port is compatible
* with a target.
* @param source
* @param target
* @return
*/
private static boolean areCompatibleFindByToTarget(final UsesPortStub source, final EObject target) {
// For a uses port on a find by, we have no type info about the port
EObject targetContainer = target.eContainer();
if (target instanceof ProvidesPortStub) {
// Allow port -> port connections since we can't check the type info
if (targetContainer instanceof ComponentInstantiation) {
return true;
} else if (targetContainer instanceof FindByStub) {
return true;
} else if (targetContainer instanceof UsesDeviceStub) {
return true;
} else {
String targetContainerType = (targetContainer != null) ? targetContainer.getClass().getCanonicalName() : "null";
throw new IllegalArgumentException("Invalid target container type: " + targetContainerType);
}
} else if (target instanceof ComponentSupportedInterfaceStub) {
if (targetContainer instanceof ComponentInstantiation) {
// TODO: For now we don't support this
return false;
} else if (targetContainer instanceof FindByStub) {
// TODO: For now we don't support this
return false;
} else if (targetContainer instanceof UsesDeviceStub) {
// TODO: For now we don't support this
return false;
} else {
String targetContainerType = (targetContainer != null) ? targetContainer.getClass().getCanonicalName() : "null";
throw new IllegalArgumentException("Invalid target container type: " + targetContainerType);
}
} else {
throw new IllegalArgumentException("Invalid target type: " + target.getClass().getCanonicalName());
}
}
/**
* Determine whether a component's port can be connected to another component's port or component supported
* interface.
* @param source The {@link UsesPortStub}
* @param target A {@link ProvidesPortStub} or {@link ComponentSupportedInterfaceStub}
* @return True if the objects are compatible for connection
* @since 7.0
*/
public static boolean areComponentToComponentCompatible(final UsesPortStub source, final EObject target) {
if (source.getUses() == null || source.getUses().getInterface() == null) {
// Something is wrong with the uses port
StatusManager.getManager().handle(
new Status(IStatus.ERROR, PluginActivator.ID, "Uses port stub was missing reference to uses port or its interface"), StatusManager.LOG);
return true;
}
if (!(source.eContainer() instanceof ComponentInstantiation)) {
throw new IllegalArgumentException("Source container was not of type ComponentInstantiation");
}
if (target instanceof ProvidesPortStub) {
final ProvidesPortStub providesStub = (ProvidesPortStub) target;
if (providesStub.getProvides() != null) {
// connection between components provides and uses stubs
if (providesStub.getProvides().getInterface() != null && source.getUses().getInterface() != null) {
return providesStub.getProvides().getInterface().isInstance(source.getUses().getInterface());
} else {
// If we can't get an interface for one side, fall back to allowing the connection
return true;
}
} else {
// Something is wrong with the provides
return false;
}
} else if (target instanceof ComponentSupportedInterfaceStub) {
Interface sourceInterface = source.getUses().getInterface();
final List<SupportsInterface> targetSupportsInterfaces = ScaEcoreUtils.getFeature(target.eContainer(), InterfacesUtil.SUPPORT_PATH);
if (targetSupportsInterfaces != null) {
// Check if any of the target's supported interfaces match the source interface
for (final SupportsInterface supportsInterface : targetSupportsInterfaces) {
if (supportsInterface.getInterface() != null && supportsInterface.getInterface().isInstance(sourceInterface)) {
return true;
}
}
// No interface match
return false;
} else {
// We have no interfaces for the target. Fallback to allowing the connection.
return true;
}
} else {
throw new IllegalArgumentException("Invalid target type");
}
}
/**
* Determine if connecting the component's uses port to a usesdevice's component supported interface is compatible.
* @param source
* @param target
* @return
* @since 7.0
*/
public static boolean areComponentToUsesDeviceComponentSupportedInterfaceCompatible(final UsesPortStub source, final ComponentSupportedInterfaceStub target) {
if (source.getUses() == null || source.getUses().getInterface() == null) {
// Something is wrong with the uses port
StatusManager.getManager().handle(
new Status(IStatus.ERROR, PluginActivator.ID, "Uses port stub was missing reference to uses port or its interface"), StatusManager.LOG);
return true;
}
// Assume the broadest possible definition of the usesdevice per REDHAWK (i.e. AggregateExecutableDevice)
// If we can't find an IDL, we'll fall back to assuming the connection points are compatible
String sourceIdl = source.getUses().getRepID();
return idlInstanceOf(sourceIdl, CF.AggregateExecutableDeviceHelper.id(), true);
}
/**
* Determine if connecting the component's uses port to a findby's component supported interface is compatible.
* @param source
* @param target
* @return
* @since 7.0
*/
public static boolean areComponentToFindByComponentSupportedInterfaceStubCompatible(final UsesPortStub source, final ComponentSupportedInterfaceStub target) {
if (source.getUses() == null || source.getUses().getInterface() == null) {
// something wrong with usesport stub
return false;
}
String sourceIdl = source.getUses().getInterface().getRepid();
// If the FindBy has a known IDL interface, check that
FindByStub findByStub = (FindByStub) target.eContainer();
String targetIdl = FindByStubUtil.getFindByStubRepId(findByStub);
if (targetIdl != null) {
return idlInstanceOf(sourceIdl, targetIdl, true);
}
// Any other findby we'll assume is compatible
return true;
}
/**
* Determines if the source IDL is an instance of the target IDL. If the IDL cannot be found, returns the specified
* fallback.
* @param sourceIdl The source IDL
* @param targetIdl The target IDL
* @param fallback The fallback value if an IDL can't be looked up in the IDL library
* @return
*/
private static boolean idlInstanceOf(String sourceIdl, String targetIdl, boolean fallback) {
// Get the IDL library
IIdlLibraryService libraryService = RedhawkUiActivator.getDefault().getIdlLibraryService();
if (libraryService == null) {
return fallback;
}
IdlLibrary library = libraryService.getLibrary();
if (library == null) {
return fallback;
}
// Special case: messaging out ports can also connect to event channels
if (ExtendedEvent.MessageEventHelper.id().equals(sourceIdl)) {
sourceIdl = org.omg.CosEventChannelAdmin.EventChannelHelper.id();
}
// Find the interface declarations for each IDL type
Identifiable sourceIdent = library.find(sourceIdl);
Identifiable targetIdent = library.find(targetIdl);
if (!(sourceIdent instanceof IdlInterfaceDcl && targetIdent instanceof IdlInterfaceDcl)) {
return fallback;
} else {
IdlInterfaceDcl sourceDcl = (IdlInterfaceDcl) sourceIdent;
IdlInterfaceDcl targetDcl = (IdlInterfaceDcl) targetIdent;
return targetDcl.isInstance(sourceDcl);
}
}
private static final EStructuralFeature[] SUPPORT_PATH = new EStructuralFeature[] { PartitioningPackage.Literals.COMPONENT_INSTANTIATION__PLACEMENT,
PartitioningPackage.Literals.COMPONENT_PLACEMENT__COMPONENT_FILE_REF, PartitioningPackage.Literals.COMPONENT_FILE_REF__FILE,
PartitioningPackage.Literals.COMPONENT_FILE__SOFT_PKG, SpdPackage.Literals.SOFT_PKG__DESCRIPTOR, SpdPackage.Literals.DESCRIPTOR__COMPONENT,
ScdPackage.Literals.SOFTWARE_COMPONENT__COMPONENT_FEATURES, ScdPackage.Literals.COMPONENT_FEATURES__SUPPORTS_INTERFACE };
}