blob: 1311c4f1b0ca09900d0fabc01d3036cfffde0446 [file] [log] [blame]
/*****************************************************************************
* Copyright (c) 2013 CEA LIST.
*
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Ansgar Radermacher ansgar.radermacher@cea.fr
*
*****************************************************************************/
package org.eclipse.papyrus.designer.deployment.tools;
import java.util.Iterator;
import java.util.Stack;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.UniqueEList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature.Setting;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.papyrus.designer.deployment.profile.Deployment.DeploymentPlan;
import org.eclipse.papyrus.designer.deployment.profile.Deployment.ImplementationProperties;
import org.eclipse.papyrus.designer.deployment.profile.Deployment.Singleton;
import org.eclipse.papyrus.designer.deployment.profile.Deployment.Target;
import org.eclipse.papyrus.designer.languages.common.base.HintUtils;
import org.eclipse.papyrus.designer.transformation.base.ElementFilter;
import org.eclipse.papyrus.designer.transformation.base.preferences.PapyrusDesignerPreferenceConstants;
import org.eclipse.papyrus.uml.tools.utils.StereotypeUtil;
import org.eclipse.uml2.common.util.UML2Util;
import org.eclipse.uml2.uml.AggregationKind;
import org.eclipse.uml2.uml.Class;
import org.eclipse.uml2.uml.Classifier;
import org.eclipse.uml2.uml.DirectedRelationship;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.Generalization;
import org.eclipse.uml2.uml.InstanceSpecification;
import org.eclipse.uml2.uml.InstanceValue;
import org.eclipse.uml2.uml.Package;
import org.eclipse.uml2.uml.PackageableElement;
import org.eclipse.uml2.uml.Property;
import org.eclipse.uml2.uml.Slot;
import org.eclipse.uml2.uml.StructuralFeature;
import org.eclipse.uml2.uml.ValueSpecification;
import org.eclipse.uml2.uml.util.UMLUtil;
/**
* Utilities around instances (within deployment plan)
*
*/
public class DepUtils {
/**
* Check whether a class is an eligible implementation for a certain node, i.e.
* has compatible requirements.
* Requires that setCurrentNode has been called earlier
*
* @param implemCandidate
* a candidate of an implementation
* @param nodes
* a list of nodes
* @return true, if passed implementation candidate is eligible for the target platform
*/
public static boolean isImplEligible(Class implemCandidate, EList<InstanceSpecification> nodes) {
if (implemCandidate.isAbstract()) {
return false;
}
if (nodes != null) {
// must fit requirements of all nodes
for (InstanceSpecification nodeInstance : nodes) {
Target target = UMLUtil.getStereotypeApplication(nodeInstance, Target.class);
if (target == null) {
// no target information on instance => try to get this
// information from the node referenced by the instance
target = UMLUtil.getStereotypeApplication(DepUtils.getClassifier(nodeInstance), Target.class);
}
if (target != null) {
ImplementationProperties implProps = UMLUtil.getStereotypeApplication(implemCandidate, ImplementationProperties.class);
if (implProps != null) {
// if (!implProps.getArch().contains(target.getTargetArch())) {
// return false;
// }
// TODO: check OS and size as well!
}
}
}
}
return true;
}
/**
* Find a sub instance via its name. This is in particular useful for connectors that cannot be
* found via a slot, since UML only supports structural features (a connector is only a feature)
* in the definingFeature attribute of a slot.
*
* @param owningInstance
* an owning instance
* @param name
* name of the sub-element (unqualified)
* @return the found sub-instance or null
*/
public static InstanceSpecification getNamedSubInstance(InstanceSpecification owningInstance, String name) {
Element cdp = owningInstance.getOwner();
String candidateName = owningInstance.getName() + "." + name; //$NON-NLS-1$
if (cdp instanceof Package) {
for (PackageableElement instance : ((Package) cdp).getPackagedElements()) {
if (instance instanceof InstanceSpecification) {
InstanceSpecification candidate = (InstanceSpecification) instance;
if (candidateName != null) {
if (candidateName.equals(candidate.getName())) {
return candidate;
}
}
}
}
}
return null;
}
/**
* Automatically choose an implementation, i.e. if the passed classifier
* (1) is already an implementation, simply return it
* (2) is an implementation group, choose the first implementation that fits the requirements
* (3) is a type: choose the first implementation among the heirs that fits the requirements
*
* @param componentType
* a component type or implementation (class, optionally abstract)
* @param nodes
* a set of instance specification representing nodes on which this component will be allocated
* @param chooser
* A chooser for an implementation in case of multiple candidates
* @return a suitable implementation
*/
public static Class chooseImplementation(Class componentType, EList<InstanceSpecification> nodes, ImplementationChooser chooser) {
// choose implementation automatically: get the first one that implements the passed type
// get reference to component model, then search all classes contained in it.
// TODO: assumption that implementations are in same package as type;
return chooseImplementation(new BasicEList<Class>(), componentType, nodes, chooser);
}
public static Class chooseImplementation(EList<Class> implList, Class componentType, EList<InstanceSpecification> nodes, ImplementationChooser chooser) {
if (!componentType.isAbstract()) {
return componentType;
} else {
for (DirectedRelationship relship : componentType.getTargetDirectedRelationships()) {
if (relship instanceof Generalization) {
Classifier source = ((Generalization) relship).getSpecific();
if (source instanceof Class) {
Class implClass = (Class) source;
if (isImplEligible(implClass, nodes)) {
implList.add(implClass);
}
}
}
}
}
if (implList.size() == 0) {
return null;
} else if (implList.size() == 1) {
return implList.get(0);
} else if (chooser != null) {
Class impl = chooser.chooseImplementation(componentType, implList);
if (impl != null) {
return impl;
}
} else if (implList.size() > 0) {
return implList.get(0);
}
return null;
}
/**
* return an instance specification for the main instance within
* a package.
*
* @param cdp
* the deployment plan
*/
public static EList<InstanceSpecification> getInstances(Package cdp) {
EList<InstanceSpecification> list = new BasicEList<InstanceSpecification>();
for (PackageableElement pe : cdp.getPackagedElements()) {
if (pe instanceof InstanceSpecification) {
InstanceSpecification is = (InstanceSpecification) pe;
list.add(is);
}
}
return list;
}
/**
* return an instance specification for the main instance within
* a package.
*
* @param cdp
* the deployment plan
*/
public static EList<InstanceSpecification> getTopLevelInstances(Package cdp) {
EList<InstanceSpecification> list = new BasicEList<InstanceSpecification>();
for (PackageableElement pe : cdp.getPackagedElements()) {
if (pe instanceof InstanceSpecification) {
InstanceSpecification is = (InstanceSpecification) pe;
if (isTopLevelInstance(is)) {
list.add(is);
}
}
}
return list;
}
/**
* Check whether an instance specification is a top-level instance, i.e. not referenced
* by another one.
*
* @param is
* an instance specification
*/
public static boolean isTopLevelInstance(InstanceSpecification is) {
return getReferencingSlots(is).size() == 0;
}
/**
* return the implementation associated with an instance specification, i.e. a
* Class.
*
* @param instance
* an instance specification
* @return the associated implementation, if it is a class or null
*/
public static Class getImplementation(InstanceSpecification instance) {
Classifier cl = getClassifier(instance);
if (cl instanceof Class) {
return (Class) cl;
}
return null;
}
/**
* Return the first classifier referenced by an instance specification. Whereas UML supports
* a set of classifiers, we assume that that an instance specification has only one.
*
* @param instance
* the instance, for which we are interested in type information
*/
public static Classifier getClassifier(InstanceSpecification instance) {
Iterator<Classifier> classifierIt = instance.getClassifiers().iterator();
// simply return the first element (if there is any)
if (classifierIt.hasNext()) {
return classifierIt.next();
}
return null;
}
/**
* Return the first instance specification within a deployment plan that instantiates a given
* classifier
*
* @param cdp
* the deployment plan
* @param cl
* the classifier
* @return the associated instance specification
*/
public static InstanceSpecification getInstanceForClassifier(Package cdp, Classifier cl) {
for (PackageableElement pe : cdp.getPackagedElements()) {
if (pe instanceof InstanceSpecification) {
InstanceSpecification is = (InstanceSpecification) pe;
if (getClassifier(is) == cl) {
return is;
}
}
}
return null;
}
/**
* Return the (unique) list of implementations that are contained within an
* instance specification
*/
public static EList<Classifier> getContainedImplementations(InstanceSpecification is) {
Iterator<InstanceSpecification> instances = getContainedInstances(is).iterator();
EList<Classifier> list = new UniqueEList<Classifier>();
while (instances.hasNext()) {
Classifier implementation = getClassifier(instances.next());
list.add(implementation);
}
return list;
}
/**
* Return the slot that is associated with a property
*
* @param is
* an instance specification (of a class having properties)
* @param property
* A property of the classifier associated with the passed instance specification
* @return the associated slot or null, if it does not exist
*/
public static Slot getSlot(InstanceSpecification is, Property property) {
for (Slot slot : is.getSlots()) {
if (slot.getDefiningFeature() == property) {
return slot;
}
}
return null;
}
/**
* Return the instance referenced by a slot value, i.e. the first instance value associated
* with a slot
*
* @param slot
* a slot which may have an associated instance value
* @return the referenced instance specification or null
*/
public static InstanceSpecification getInstance(Slot slot) {
for (ValueSpecification value : slot.getValues()) {
// instances are accessible via ValueSpecification subclass InstanceValue
if (value instanceof InstanceValue) {
return ((InstanceValue) value).getInstance();
}
}
return null;
}
/**
* This method returns the instances contained within a composite instance
* specification for an assembly.
*/
public static EList<InstanceSpecification> getContainedInstances(InstanceSpecification is) {
EList<InstanceSpecification> contained = new BasicEList<InstanceSpecification>();
for (Slot slot : is.getSlots()) {
InstanceSpecification instance = getInstance(slot);
if (instance != null) {
contained.add(instance);
}
}
return contained;
}
/**
* This method returns the instances contained within a composite instance
* specification for an assembly. Unlike @see getContainedInstances, this method only
* returns contained instances that are not shared.
*
* @param is
* an instance specification
* @return the list of contained sub-instances
*/
public static EList<InstanceSpecification> getContainedNonSharedInstances(InstanceSpecification is) {
EList<InstanceSpecification> contained = new BasicEList<InstanceSpecification>();
for (Slot slot : is.getSlots()) {
InstanceSpecification instance = getInstance(slot);
if ((instance != null) && !DepUtils.isShared(slot)) {
contained.add(instance);
}
}
return contained;
}
/**
* Return all slots that reference an instance specification
*
* @param is
* an instance specification
* @return the list of slots referencing this instance (via their value specification)
*/
public static EList<Slot> getReferencingSlots(InstanceSpecification is) {
EList<Slot> list = new BasicEList<Slot>();
for (Setting setting : UML2Util.getNonNavigableInverseReferences(is)) {
EObject eObj = setting.getEObject();
if (eObj instanceof ValueSpecification) {
ValueSpecification vs = (ValueSpecification) eObj;
Element owner = vs.getOwner();
if (owner instanceof Slot) {
list.add((Slot) owner);
}
}
}
return list;
}
/**
* Return a slot for a given instance specification. The slot is the first one in a list of slots
* whose value points to the passed instance.
*
* @param is
* an instance that is contained within an composite (i.e. that
* belongs to a part of this composite).
* @return the first slot referencing the passed instance
*/
public static Slot getParentSlot(InstanceSpecification is) {
for (Slot slot : getReferencingSlots(is)) {
if (slot.getDefiningFeature() instanceof Property) {
if (((Property) slot.getDefiningFeature()).getAggregation() == AggregationKind.COMPOSITE_LITERAL) {
return slot;
}
}
}
return null;
}
/**
* Return an instance specification that refers to the composite in which the
* passed instance is contained
*
* @param is
* an instance that is contained within an composite (i.e. that
* belongs to a part of this composite).
* @return the first instance within the same deployment plan referencing this instance
*/
public static InstanceSpecification getParentIS(InstanceSpecification is) {
Slot parentSlot = getParentSlot(is);
if (parentSlot != null) {
return parentSlot.getOwningInstance();
}
return null;
}
/**
* Return the access path in terms of slots to an instance specification, i.e. the
* set of slots starting with the slot within the main instance that identifies the next
* instance until arriving at the passed instance.
*
* @param is an instance specification
* @return a list of slots forming an access path
*/
public static Stack<Slot> getAccessPath(InstanceSpecification is) {
Stack<Slot> path = new Stack<Slot>();
while (is != null) {
Slot parentSlot = getParentSlot(is);
if (parentSlot == null) {
break;
}
path.insertElementAt(parentSlot, 0);
is = parentSlot.getOwningInstance();
}
return path;
}
/**
* Return true, if an instance is shared
*
* @param slot a slot
* @return true, if shared
*/
public static boolean isShared(Slot slot) {
StructuralFeature df = slot.getDefiningFeature();
if (df instanceof Property) {
return ((Property) df).getAggregation() == AggregationKind.SHARED_LITERAL;
}
return false;
}
/**
* Return the target language when given the mainInstance
*
* @param mainInstance
* the mainInstance of an application
* @return target language
*/
public static String getTargetLanguage(InstanceSpecification mainInstance) {
Classifier cl = DepUtils.getClassifier(mainInstance);
return HintUtils.getLanguageFromElement(cl);
}
/**
* Get all instances within a package that comply with a filter criterion. Recurse into sub-packages.
*
* @param pkg
* Starting package for search
* @param instanceList
* list of instances
* @param filter
* filter criterion.
*/
public static void getAllInstances(Package pkg, EList<InstanceSpecification> instanceList, ElementFilter filter) {
for (PackageableElement el : pkg.getPackagedElements()) {
if (el instanceof Package) {
getAllInstances((Package) el, instanceList, filter);
} else if (el instanceof InstanceSpecification) {
InstanceSpecification instance = (InstanceSpecification) el;
if (filter.acceptElement(instance)) {
instanceList.add(instance);
}
}
}
}
/**
* Return the first value for a slot.
*
* @param slot
* the slot for which the first value should be returned.
* @return the first value referenced by a slot
*/
public static ValueSpecification firstValue(Slot slot) {
if (slot.getValues().size() > 0) {
return slot.getValues().get(0);
}
return null;
}
/**
* Return true, if aggregation type "none" should be treated in the same way
* as aggregation kind "composite". This information is used to create instances
* in compositions
*
* @return true, if aggregation type "none" should be as "composite"
*/
public static boolean treatNoneAsComposite() {
IPreferenceStore store = Activator.getDefault().getPreferenceStore();
return store.getBoolean(PapyrusDesignerPreferenceConstants.P_TREAT_NONE_AS_COMPOSITE);
}
/**
* @return true, if all attributes of a class are considered as configuration attributes.
*/
public static boolean allAttributesAreConfigAttributs() {
IPreferenceStore store = Activator.getDefault().getPreferenceStore();
return store.getBoolean(PapyrusDesignerPreferenceConstants.P_ALL_ATTRIBUTES_ARE_CONFIG_ATTRIBUTES);
}
public static EList<Property> getParts(Class implementation) {
if (treatNoneAsComposite()) {
EList<Property> parts = new BasicEList<Property>();
for (Property part : implementation.getAttributes()) {
if (part.getAggregation() != AggregationKind.SHARED_LITERAL) {
parts.add(part);
}
}
return parts;
} else {
return implementation.getParts();
}
}
/**
* return true, if a component (implementation) is a composite, i.e. has
* inner parts
*/
public static boolean isComposite(Class implementation) {
return (getParts(implementation).size() > 0);
}
/**
* @param attribute
* an attribute
* @return true, if the aggregation kind is considered as a composition
*/
public static boolean isComposition(Property attribute) {
if (treatNoneAsComposite()) {
return (attribute.getAggregation() != AggregationKind.SHARED_LITERAL);
} else {
return (attribute.getAggregation() == AggregationKind.COMPOSITE_LITERAL);
}
}
public static boolean isSingleton(Class component) {
return StereotypeUtil.isApplied(component, Singleton.class);
}
/**
* @param deploymentPlanCandidate
* @return true, if the passed package is a deployment plan
*
*/
public static boolean isDeploymentPlan(Package deploymentPlanCandidate) {
return StereotypeUtil.isApplied(deploymentPlanCandidate, DeploymentPlan.class);
}
}