AadlBusinessObjectProvider.java
/**
* Copyright (c) 2004-2025 Carnegie Mellon University and others. (see Contributors file).
* All Rights Reserved.
*
* NO WARRANTY. ALL MATERIAL IS FURNISHED ON AN "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY
* KIND, EITHER EXPRESSED OR IMPLIED, AS TO ANY MATTER INCLUDING, BUT NOT LIMITED TO, WARRANTY OF FITNESS FOR PURPOSE
* OR MERCHANTABILITY, EXCLUSIVITY, OR RESULTS OBTAINED FROM USE OF THE MATERIAL. CARNEGIE MELLON UNIVERSITY DOES NOT
* MAKE ANY WARRANTY OF ANY KIND WITH RESPECT TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.
*
* This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
* SPDX-License-Identifier: EPL-2.0
*
* Created, in part, with funding and support from the United States Government. (see Acknowledgments file).
*
* This program includes and/or can make use of certain third party source code, object code, documentation and other
* files ("Third Party Software"). The Third Party Software that is used by this program is dependent upon your system
* configuration. By using this program, You agree to comply with any and all relevant Third Party Software terms and
* conditions contained in any such Third Party Software or separate license file distributed with such Third Party
* Software. The parties who own the Third Party Software ("Third Party Licensors") are intended third party benefici-
* aries to this license with respect to the terms applicable to their Third Party Software. Third Party Software li-
* censes only apply to the Third Party Software and not any other portion of this program or this program as a whole.
*/
package org.osate.ge.aadl2.internal;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtext.resource.IEObjectDescription;
import org.osate.aadl2.Aadl2Package;
import org.osate.aadl2.AadlPackage;
import org.osate.aadl2.AnnexLibrary;
import org.osate.aadl2.AnnexSubclause;
import org.osate.aadl2.BehavioredImplementation;
import org.osate.aadl2.Classifier;
import org.osate.aadl2.ComponentClassifier;
import org.osate.aadl2.ComponentImplementation;
import org.osate.aadl2.ComponentPrototype;
import org.osate.aadl2.ComponentType;
import org.osate.aadl2.Connection;
import org.osate.aadl2.DefaultAnnexLibrary;
import org.osate.aadl2.DefaultAnnexSubclause;
import org.osate.aadl2.FeatureGroup;
import org.osate.aadl2.FeatureGroupType;
import org.osate.aadl2.GroupExtension;
import org.osate.aadl2.ImplementationExtension;
import org.osate.aadl2.ModeTransition;
import org.osate.aadl2.NamedElement;
import org.osate.aadl2.PackageSection;
import org.osate.aadl2.Realization;
import org.osate.aadl2.Subcomponent;
import org.osate.aadl2.SubprogramAccess;
import org.osate.aadl2.SubprogramCall;
import org.osate.aadl2.SubprogramCallSequence;
import org.osate.aadl2.SubprogramClassifier;
import org.osate.aadl2.SubprogramImplementation;
import org.osate.aadl2.SubprogramProxy;
import org.osate.aadl2.SubprogramSubcomponent;
import org.osate.aadl2.SubprogramSubcomponentType;
import org.osate.aadl2.SubprogramType;
import org.osate.aadl2.TypeExtension;
import org.osate.aadl2.instance.ComponentInstance;
import org.osate.aadl2.instance.ConnectionInstance;
import org.osate.aadl2.instance.ConnectionReference;
import org.osate.aadl2.instance.FeatureInstance;
import org.osate.ge.BusinessObjectContext;
import org.osate.ge.BusinessObjectProvider;
import org.osate.ge.BusinessObjectProviderContext;
import org.osate.ge.aadl2.internal.model.SubprogramCallOrder;
import org.osate.ge.aadl2.internal.model.Tag;
import org.osate.ge.aadl2.internal.util.AadlFeatureUtil;
import org.osate.ge.aadl2.internal.util.AadlSubcomponentUtil;
import org.osate.ge.aadl2.internal.util.AadlSubprogramCallUtil;
import org.osate.ge.aadl2.internal.util.AgeAadlUtil;
import org.osate.ge.aadl2.ui.AadlModelAccessUtil;
import org.osate.ge.internal.model.BusinessObjectProxy;
import org.osate.ge.internal.services.ExtensionRegistryService;
public class AadlBusinessObjectProvider implements BusinessObjectProvider {
private final ModeTransitionTriggerHandler mttHandler = new ModeTransitionTriggerHandler();
@Override
public Stream<?> getChildBusinessObjects(final BusinessObjectProviderContext ctx) {
final Object bo = ctx.getBusinessObjectContext().getBusinessObject();
// An IProject is specified as the business object for contextless diagrams.
if (bo instanceof IProject) { // Special handling for project
final IProject project = (IProject) bo;
// Perform an incremental project build to ensure new packages are included.
try {
project.build(IncrementalProjectBuilder.INCREMENTAL_BUILD, new NullProgressMonitor());
} catch (CoreException e) {
throw new RuntimeException(e);
}
Stream<Object> packages = getPackages(project);
// If no packages were found, assume that the project needs to be built. This can happen if the Eclipse process is forcefully terminated.
if (packages == null) {
try {
project.build(IncrementalProjectBuilder.FULL_BUILD, new NullProgressMonitor());
} catch (CoreException e) {
throw new RuntimeException(e);
}
packages = getPackages(project);
}
return packages;
} else if (bo instanceof AadlPackage) {
return getChildren((AadlPackage) bo, ctx.getExtensionRegistry());
} else if(bo instanceof Classifier) {
return getChildren((Classifier) bo, true, ctx.getExtensionRegistry());
} else if(bo instanceof FeatureGroup) {
final FeatureGroupType fgt = AadlFeatureUtil.getFeatureGroupType(ctx.getBusinessObjectContext(), (FeatureGroup) bo);
return fgt == null ? Stream.empty() : AadlFeatureUtil.getAllFeatures(fgt).stream();
} else if(bo instanceof Subcomponent) {
return getChildren((Subcomponent) bo, ctx.getBusinessObjectContext(), ctx.getExtensionRegistry());
} else if(bo instanceof SubprogramCall) {
return getChildren((SubprogramCall)bo);
} else if(bo instanceof SubprogramCallSequence) {
return getChildren((SubprogramCallSequence)bo);
} else if(bo instanceof ModeTransition) {
final ModeTransition mt = ((ModeTransition) bo);
final String modeTransitionTriggersDesc = mt.getOwnedTriggers().stream().map(mtt -> mttHandler.getName(mtt))
.collect(Collectors.joining(","));
return Stream.concat(mt.getOwnedTriggers().stream(),
Stream.of(new Tag(Tag.KEY_MODE_TRANSITION_TRIGGERS, modeTransitionTriggersDesc)));
} else if(bo instanceof ComponentInstance) {
return getChildren((ComponentInstance)bo);
} else if(bo instanceof FeatureInstance) {
return ((FeatureInstance)bo).getFeatureInstances().stream();
} else if(bo instanceof Connection) {
if(!((Connection) bo).isAllBidirectional()) {
return Stream.of(new Tag(Tag.KEY_UNIDIRECTIONAL, null));
}
} else if(bo instanceof ConnectionInstance) {
if(!((ConnectionInstance) bo).isBidirectional()) {
return Stream.of(new Tag(Tag.KEY_UNIDIRECTIONAL, null));
}
}
return Stream.empty();
}
/**
* Returns null if no packages are found. This is needed so that it can be determined if the project needs to be rebuilt.
* @param project
*/
private static Stream<Object> getPackages(final IProject project) {
Stream.Builder<Object> packages = null;
for (final IEObjectDescription desc : AadlModelAccessUtil.getAllEObjectsByType(project,
Aadl2Package.eINSTANCE.getAadlPackage())) {
if (packages == null) {
packages = Stream.builder();
}
final String pkgQualifiedName = desc.getQualifiedName().toString("::");
packages.add(new BusinessObjectProxy(
"Aadl Package "
+ pkgQualifiedName,
PackageProxyHandler.PROXY_TYPE_ID,
AadlReferenceUtil.getCanonicalReferenceForPackage(
pkgQualifiedName),
AadlReferenceUtil.getRelativeReferenceForPackage(pkgQualifiedName)));
}
return packages == null ? null : packages.build();
}
// Declarative Model
private static Stream<Object> getChildren(final AadlPackage pkg, final ExtensionRegistryService extRegistryService) {
// Build a list of all named elements in the public and private sections of the package
final Set<Object> children = new HashSet<>();
populateChildren(pkg.getPublicSection(), children, extRegistryService);
populateChildren(pkg.getPrivateSection(), children, extRegistryService);
return children.stream();
}
private static void populateChildren(final PackageSection ps, final Set<Object> children,
final ExtensionRegistryService extRegistryService) {
if(ps == null) {
return;
}
children.addAll(ps.getOwnedClassifiers());
for(final AnnexLibrary annexLibrary : ps.getOwnedAnnexLibraries()) {
final NamedElement parsedAnnexLibrary = getParsedAnnexLibrary(annexLibrary);
final boolean specializedHandling = parsedAnnexLibrary != null && extRegistryService.getApplicableBusinessObjectHandler(parsedAnnexLibrary) != null;
// Only contribute the annex object if a BOH for the annex does not exist. The annex plugin is expected to contribute the object as needed.
if (!specializedHandling) {
children.add(annexLibrary);
}
}
}
private static NamedElement getParsedAnnexLibrary(final NamedElement annexLibrary) {
if(annexLibrary instanceof DefaultAnnexLibrary) {
final NamedElement parsedLib = ((DefaultAnnexLibrary) annexLibrary).getParsedAnnexLibrary();
// Don't return libraries which inherit from DefaultAnnexLibrary
if(parsedLib instanceof DefaultAnnexLibrary) {
return null;
}
return parsedLib;
}
return null;
}
private static AnnexSubclause getParsedAnnexSubclause(final NamedElement annexSubclause) {
if(annexSubclause instanceof DefaultAnnexSubclause) {
final AnnexSubclause parsedSubclause = ((DefaultAnnexSubclause) annexSubclause).getParsedAnnexSubclause();
// Don't return libraries which inherit from DefaultAnnexSubclause
if(parsedSubclause instanceof DefaultAnnexSubclause) {
return null;
}
return parsedSubclause;
}
return null;
}
private static Stream<?> getChildren(final Subcomponent sc,
final BusinessObjectContext scBoc,
final ExtensionRegistryService extRegistryService) {
final ComponentClassifier cc = AadlSubcomponentUtil.getComponentClassifier(scBoc, sc);
if(cc == null) {
return null;
}
Stream<?> results = getChildren(cc, false, extRegistryService);
final String scTypeTxt = AadlSubcomponentUtil.getSubcomponentTypeDescription(sc, scBoc);
if(scTypeTxt != null) {
results = Stream.concat(results, Stream.of(new Tag(Tag.KEY_SUBCOMPONENT_TYPE, scTypeTxt)));
}
return results;
}
private static Stream<?> getChildren(final SubprogramCall call) {
final SubprogramType subprogramType = getSubprogramType(call);
Stream<?> results = Stream.empty();
if(subprogramType != null) {
results = Stream.concat(results,
Stream.concat(AadlFeatureUtil.getAllDeclaredFeatures(subprogramType).stream(),
subprogramType.getAllFlowSpecifications().stream()));
}
final String calledSubprogramReference = AadlSubprogramCallUtil.getCalledSubprogramDescription(call);
if(calledSubprogramReference != null) {
results = Stream.concat(results, Stream.of(new Tag(Tag.KEY_SUBPROGRAM_CALL_CALLED_SUBPROGRAM, calledSubprogramReference)));
}
return results;
}
private static Stream<?> getChildren(final SubprogramCallSequence cs) {
return Stream.concat(cs.getOwnedSubprogramCalls().stream(), SubprogramCallOrder.getSubprogramCallOrders(cs.getOwnedSubprogramCalls()).stream());
}
/**
* Finds and returns the SubprogramType associated with the specified model element.
* @param element
* @return
*/
private static SubprogramType getSubprogramType(final EObject element) {
if(element instanceof SubprogramType) {
return (SubprogramType)element;
} else if(element instanceof SubprogramCall) {
return getSubprogramType(((SubprogramCall) element).getCalledSubprogram());
} else if(element instanceof SubprogramImplementation) {
return ((SubprogramImplementation)element).getType();
} else if(element instanceof SubprogramSubcomponent) {
return getSubprogramType(((SubprogramSubcomponent) element).getSubprogramSubcomponentType());
} else if(element instanceof SubprogramAccess) {
final SubprogramSubcomponentType scType = ((SubprogramAccess) element).getSubprogramFeatureClassifier();
return getSubprogramType(scType);
} else if(element instanceof SubprogramProxy) {
final SubprogramClassifier scClassifier = ((SubprogramProxy) element).getSubprogramClassifier();
return getSubprogramType(scClassifier);
} else if(element instanceof ComponentPrototype) {
final ComponentClassifier constrainingClassifier = ((ComponentPrototype) element).getConstrainingClassifier();
return getSubprogramType(constrainingClassifier);
} else {
// Unsupported case. Possibly prototype
return null;
}
}
private static Stream<?> getChildren(final Classifier classifier,
final boolean includeGeneralizations,
final ExtensionRegistryService extRegistryService) {
Stream<?> children = Stream.empty();
// Shapes
children = Stream.concat(children, AadlFeatureUtil.getAllDeclaredFeatures(classifier).stream());
if(classifier instanceof ComponentImplementation) {
final ComponentImplementation ci = (ComponentImplementation)classifier;
children = Stream.concat(children, AgeAadlUtil.getAllInternalFeatures(ci).stream());
children = Stream.concat(children, AgeAadlUtil.getAllProcessorFeatures(ci).stream());
children = Stream.concat(children, ci.getAllSubcomponents().stream());
}
if(classifier instanceof BehavioredImplementation) {
children = Stream.concat(children, AgeAadlUtil.getAllSubprogramCallSequences((BehavioredImplementation)classifier).stream());
}
if(classifier instanceof ComponentClassifier) {
children = Stream.concat(children, ((ComponentClassifier)classifier).getAllModes().stream());
}
// Annex Subclauses
final Stream.Builder<AnnexSubclause> subclauseStreamBuilder = Stream.builder();
for(final AnnexSubclause annexSubclause : getAllDefaultAnnexSubclauses(classifier)) {
final AnnexSubclause parsedAnnexSubclause = getParsedAnnexSubclause(annexSubclause);
final boolean specializedHandling = parsedAnnexSubclause != null && extRegistryService.getApplicableBusinessObjectHandler(parsedAnnexSubclause) != null;
// Only contribute the annex object if a BOH for the annex does not exist. The annex plugin is expected to contribute the object as needed.
if (!specializedHandling) {
subclauseStreamBuilder.add(annexSubclause);
}
}
children = Stream.concat(children, subclauseStreamBuilder.build());
// Connections
if(classifier instanceof ComponentClassifier) {
children = Stream.concat(children, ((ComponentClassifier)classifier).getAllModeTransitions().stream());
}
if(classifier instanceof ComponentImplementation) {
children = Stream.concat(children, ((ComponentImplementation)classifier).getAllConnections().stream());
}
final ComponentType componentType;
if(classifier instanceof ComponentType) {
componentType = (ComponentType)classifier;
} else if(classifier instanceof ComponentImplementation) {
componentType = ((ComponentImplementation)classifier).getType();
} else {
componentType = null;
}
if(componentType != null) {
children = Stream.concat(children, componentType.getAllFlowSpecifications().stream());
}
// Add generalizations
if(includeGeneralizations) {
if(classifier instanceof ComponentType) {
final ComponentType ct = ((ComponentType)classifier);
final TypeExtension te = ct.getOwnedExtension();
if(te != null) {
children = Stream.concat(children, Stream.of(te));
}
} else if(classifier instanceof ComponentImplementation) {
final ComponentImplementation componentImplementation = ((ComponentImplementation)classifier);
// Implementation Extension
final ImplementationExtension ie = componentImplementation.getOwnedExtension();
if(ie != null) {
children = Stream.concat(children, Stream.of(ie));
} else {
// Realization
final Realization realization = componentImplementation.getOwnedRealization();
if (realization != null) {
children = Stream.concat(children, Stream.of(realization));
}
}
} else if(classifier instanceof FeatureGroupType) {
final FeatureGroupType featureGroupType = ((FeatureGroupType)classifier);
final GroupExtension ge = featureGroupType.getOwnedExtension();
if(ge != null) {
children = Stream.concat(children, Stream.of(ge));
}
}
}
return children;
}
/**
* Returns all the default annex subclauses owned by a classifier or any extended or implemented classifiers.
* @param topClassifier
* @return
*/
private static EList<AnnexSubclause> getAllDefaultAnnexSubclauses(final Classifier topClassifier) {
final EList<AnnexSubclause> result = new BasicEList<AnnexSubclause>();
if(topClassifier == null) {
return result;
}
final EList<Classifier> classifiers = topClassifier.getSelfPlusAllExtended();
if (topClassifier instanceof ComponentImplementation) {
ComponentType ct = ((ComponentImplementation) topClassifier).getType();
if(ct != null) { // May be null in the case of an invalid model.
final EList<Classifier> tclassifiers = ct.getSelfPlusAllExtended();
classifiers.addAll(tclassifiers);
}
}
for (Classifier classifier : classifiers) {
result.addAll(classifier.getOwnedAnnexSubclauses());
}
return result;
}
// Instance Model
private static Stream<?> getChildren(ComponentInstance ci) {
Stream.Builder<Object> connectionReferenceStreamBuilder = Stream.builder();
for(final ConnectionInstance connectionInstance : ci.getConnectionInstances()) {
for(final ConnectionReference cr : connectionInstance.getConnectionReferences()) {
connectionReferenceStreamBuilder.add(cr);
}
}
return Stream.of(ci.getModeInstances().stream(),
ci.getModeTransitionInstances().stream(),
ci.getFlowSpecifications().stream(), ci.getComponentInstances().stream(),
ci.getFeatureInstances().stream(), connectionReferenceStreamBuilder.build()).flatMap(o -> o);
}
}