blob: 088d7c7d4e5553c28741235e86df11b9429323f7 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006 - 2012, 2017 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:
* CEA LIST - initial API and implementation
*******************************************************************************/
package org.eclipse.papyrus.designer.languages.cpp.codegen.utils;
import java.util.List;
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.papyrus.designer.languages.common.base.GenUtils;
import org.eclipse.papyrus.designer.languages.common.profile.Codegen.NoCodeGen;
import org.eclipse.papyrus.designer.languages.cpp.profile.C_Cpp.ExternLibrary;
import org.eclipse.papyrus.designer.languages.cpp.profile.C_Cpp.External;
import org.eclipse.papyrus.designer.languages.cpp.profile.C_Cpp.Inline;
import org.eclipse.papyrus.designer.languages.cpp.profile.C_Cpp.Ptr;
import org.eclipse.papyrus.designer.languages.cpp.profile.C_Cpp.Ref;
import org.eclipse.uml2.uml.Classifier;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.Enumeration;
import org.eclipse.uml2.uml.Interface;
import org.eclipse.uml2.uml.Operation;
import org.eclipse.uml2.uml.Package;
import org.eclipse.uml2.uml.PrimitiveType;
import org.eclipse.uml2.uml.util.UMLUtil;
/**
* A set of utility functions related to C++ classes
*
*/
public class CppClassUtils {
public enum Position {
FOR_HEADER, FOR_BODY, FOR_BOTH
};
/**
* Calculate the list of classifiers that needs to be included in a header file
*
* @param currentClass
* @return a list of classifiers (to be included)
*/
public static EList<Classifier> includedClassifiers(Classifier currentClass) {
// Retrieve package used by current package (dependencies)
// use a unique list to avoid duplicates
EList<Classifier> usedClasses = new UniqueEList<Classifier>();
// Lists of excluded/included stereotypes
EList<Class<? extends EObject>> ptrRefStereotypes = new BasicEList<Class<? extends EObject>>();
ptrRefStereotypes.add(Ptr.class);
ptrRefStereotypes.add(Ref.class);
EList<Class<? extends EObject>> noCodeGenInlineStereotypes = new BasicEList<Class<? extends EObject>>();
noCodeGenInlineStereotypes.add(NoCodeGen.class);
noCodeGenInlineStereotypes.add(Inline.class);
EList<Class<? extends EObject>> inlineStereotypes = new BasicEList<Class<? extends EObject>>();
inlineStereotypes.add(Inline.class);
EList<Class<? extends EObject>> noCodeGenStereotypes = new BasicEList<Class<? extends EObject>>();
noCodeGenStereotypes.add(NoCodeGen.class);
// class attributes dependencies (non-ptr and non-ref)
usedClasses.addAll(GenUtils.getTypesViaAttributes(currentClass, ptrRefStereotypes, null, true, true));
addEnumerationsPrimitiveTypesExternalTypes(usedClasses, GenUtils.getTypesViaAttributes(currentClass));
// class inline operation parameters dependencies (non-ptr and non-ref)
usedClasses.addAll(GenUtils.getTypesViaOperations(currentClass, noCodeGenStereotypes, inlineStereotypes, ptrRefStereotypes, null, true));
addEnumerationsPrimitiveTypesExternalTypes(usedClasses, GenUtils.getTypesViaOperations(currentClass));
// inner classifier attribute dependencies (non-ptr and non-ref)
usedClasses.addAll(GenUtils.getInnerClassifierTypesViaAttributes(currentClass, ptrRefStereotypes, null, true, true));
addEnumerationsPrimitiveTypesExternalTypes(usedClasses, GenUtils.getInnerClassifierTypesViaAttributes(currentClass));
// inner classifier operation parameters dependencies (non-ptr and non-ref)
usedClasses.addAll(GenUtils.getInnerClassifierTypesViaOperations(currentClass, noCodeGenStereotypes, inlineStereotypes, ptrRefStereotypes, null, true));
addEnumerationsPrimitiveTypesExternalTypes(usedClasses, GenUtils.getInnerClassifierTypesViaOperations(currentClass));
// realized interface dependencies
if (currentClass instanceof org.eclipse.uml2.uml.Class) {
org.eclipse.uml2.uml.Class clazz = (org.eclipse.uml2.uml.Class) currentClass;
EList<Interface> implementedInterfaces = clazz.getImplementedInterfaces();
usedClasses.addAll(implementedInterfaces);
}
// dependencies and associations
usedClasses.addAll(GenUtils.getTypesViaRelationshipsNoDeps(currentClass));
// template parameters are declared locally (if owned) and do not correspond to a file
// that can be included
usedClasses.removeAll(GenUtils.getTemplateParameteredElements(currentClass));
return usedClasses;
}
/**
* Calculate the list of classifiers that needs to be included in a body file
*
* @param currentClass
* @return a list of classifiers (to be included)
*/
public static EList<Classifier> includedImplementationClassifiers(Classifier currentClass) {
// Retrieve package used by current package (dependencies)
// use a unique list to avoid duplicates
EList<Classifier> usedClasses = new UniqueEList<Classifier>();
// Include all declared classifiers in header
// Make sure to not include classifiers already included in header
usedClasses.addAll(declaredClassifiers(currentClass));
usedClasses.removeAll(includedClassifiers(currentClass));
// dependency relationships
usedClasses.addAll(GenUtils.getTypesViaDependencies(currentClass));
// template parameters are declared locally (if owned) and do not correspond to a file
// that can be included
usedClasses.removeAll(GenUtils.getTemplateParameteredElements(currentClass));
return usedClasses;
}
/**
* Calculate the list of classifiers that should be forward-declared in a header file
* The background is that forward declarations could avoid inclusion cycles
*
* @param currentClass
* @return a list of classifiers (to be included)
*/
public static EList<Classifier> declaredClassifiers(Classifier currentClass) {
EList<Classifier> usedClasses = new UniqueEList<Classifier>();
// List of excluded/included stereotypes
EList<Class<? extends EObject>> ptrRefStereotypes = new BasicEList<Class<? extends EObject>>();
ptrRefStereotypes.add(Ptr.class);
ptrRefStereotypes.add(Ref.class);
EList<Class<? extends EObject>> inlineStereotypes = new BasicEList<Class<? extends EObject>>();
inlineStereotypes.add(Inline.class);
EList<Class<? extends EObject>> noCodeGenStereotypes = new BasicEList<Class<? extends EObject>>();
noCodeGenStereotypes.add(NoCodeGen.class);
// class attributes dependencies (only ptr and ref and shared aggregation)
usedClasses.addAll(GenUtils.getTypesViaAttributes(currentClass, null, ptrRefStereotypes, false, false));
usedClasses.addAll(GenUtils.getTypesViaSharedAggregationAttributes(currentClass));
// operation parameters dependencies
usedClasses.addAll(GenUtils.getTypesViaOperations(currentClass));
usedClasses.removeAll(GenUtils.getTypesViaOperations(currentClass, noCodeGenStereotypes, inlineStereotypes, ptrRefStereotypes, null, true)); // Remove inline operation parameter types that have been included previously
// no-specification opaque behavior dependencies
usedClasses.addAll(GenUtils.getTypesViaOpaqueBehaviors(currentClass));
// inner classifier attribute dependencies (only ptr and ref and shared aggregation)
usedClasses.addAll(GenUtils.getInnerClassifierTypesViaAttributes(currentClass, null, ptrRefStereotypes, false, false));
usedClasses.addAll(GenUtils.getInnerClassifierTypesViaSharedAggregationAttributes(currentClass));
// inner classifier parameters dependencies
usedClasses.addAll(GenUtils.getInnerClassifierTypesViaOperations(currentClass));
usedClasses.removeAll(GenUtils.getInnerClassifierTypesViaOperations(currentClass, noCodeGenStereotypes, inlineStereotypes, ptrRefStereotypes, null, true)); // Remove inner classifier inline operation parameter types that have been included previously
// inner classifier no-specification opaque behavior dependencies
usedClasses.addAll(GenUtils.getInnerClassifierTypesViaOpaqueBehaviors(currentClass));
// TODO inline operation body dependencies: how?
// template parameters are declared locally (if owned) and do not correspond to a file
// that can be included
usedClasses.removeAll(GenUtils.getTemplateParameteredElements(currentClass));
// Remove enumerations and primitive types
List<Classifier> enumerationsAndPrimitiveTypes = new UniqueEList<Classifier>();
for (Classifier classifier : usedClasses) {
if ((classifier instanceof Enumeration) || (classifier instanceof PrimitiveType)) {
if (classifier.getOwner() instanceof Package) {
enumerationsAndPrimitiveTypes.add(classifier);
}
}
}
usedClasses.removeAll(enumerationsAndPrimitiveTypes);
return usedClasses;
}
/**
* Calculate the "using" context of this classifier. The returned variable is stored in a
* static variable and remains accessible. This functions must be called before accessing the
* current context (method currentUsingClassifiers)
*
* @param currentClass
* @param position
* determine whether we generate
* a list of using classifiers for the header file, body file or both
* @return a list of classifiers for which "using" statements will be generated (if that option is active)
*/
public static EList<Classifier> usingClassifiers(Classifier currentClass, Position position) {
EList<Classifier> usingClasses = new UniqueEList<Classifier>();
if (position == Position.FOR_HEADER || position == Position.FOR_BOTH) {
usingClasses.addAll(includedClassifiers(currentClass));
usingClasses.addAll(declaredClassifiers(currentClass));
}
if (position == Position.FOR_BODY || position == Position.FOR_BOTH) {
usingClasses.addAll(includedImplementationClassifiers(currentClass));
}
EList<Classifier> classesToRemove = new UniqueEList<Classifier>();
for (Classifier classifier : usingClasses) {
if (classifier.getOwner() instanceof Classifier) {
classesToRemove.add(classifier);
} else if (UMLUtil.getStereotypeApplication(classifier, External.class) != null) {
classesToRemove.add(classifier);
} else if (classifier.getOwner() instanceof Package
&& UMLUtil.getStereotypeApplication(classifier.getOwner(), ExternLibrary.class) != null) {
classesToRemove.add(classifier);
}
}
usingClasses.removeAll(classesToRemove);
return usingClasses;
}
/**
* Retrieve the list of operations of classes nested in the current class
* without the operations directly owned by the current class
*
* @param currentClass
* @return a list of operations
*/
public static EList<Operation> nestedOperations(Classifier currentClass) {
EList<Operation> nestedOperations = new UniqueEList<Operation>();
for (Element element : currentClass.allOwnedElements()) {
if (element instanceof Operation && !(element.getOwner().equals(currentClass))) {
nestedOperations.add((Operation) element);
}
}
return nestedOperations;
}
private static void addEnumerationsPrimitiveTypesExternalTypes(List<Classifier> usedClasses, List<Classifier> unfilteredClasses) {
if (usedClasses != null && unfilteredClasses != null) {
for (Classifier classifier : unfilteredClasses) {
if ((classifier instanceof Enumeration) || (classifier instanceof PrimitiveType)) {
if (classifier.getOwner() instanceof Package) {
usedClasses.add(classifier);
}
} else if (GenUtils.hasStereotype(classifier, External.class)) {
usedClasses.add(classifier);
}
}
}
}
}