CreateErrorPropagationPaletteCommand.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.errormodel.ui.palette;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.BiConsumer;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.widgets.Display;
import org.osate.aadl2.AadlPackage;
import org.osate.aadl2.DirectionType;
import org.osate.aadl2.Feature;
import org.osate.aadl2.NamedElement;
import org.osate.ge.BusinessObjectContext;
import org.osate.ge.StringUtil;
import org.osate.ge.aadl2.AadlGraphicalEditorException;
import org.osate.ge.errormodel.combined.CombinedErrorModelSubclause;
import org.osate.ge.errormodel.combined.PropagationNode;
import org.osate.ge.errormodel.combined.PropagationTreeUtil;
import org.osate.ge.errormodel.model.KeywordPropagationPoint;
import org.osate.ge.errormodel.model.KeywordPropagationPointType;
import org.osate.ge.errormodel.ui.ErrorModelUiUtil;
import org.osate.ge.errormodel.util.ErrorModelGeUtil;
import org.osate.ge.operations.Operation;
import org.osate.ge.operations.StepResultBuilder;
import org.osate.ge.palette.BasePaletteCommand;
import org.osate.ge.palette.GetTargetedOperationContext;
import org.osate.ge.palette.TargetedPaletteCommand;
import org.osate.xtext.aadl2.errormodel.errorModel.ErrorModelFactory;
import org.osate.xtext.aadl2.errormodel.errorModel.ErrorModelSubclause;
import org.osate.xtext.aadl2.errormodel.errorModel.ErrorPropagation;
import org.osate.xtext.aadl2.errormodel.errorModel.FeatureorPPReference;
import org.osate.xtext.aadl2.errormodel.errorModel.PropagationPoint;
import com.google.common.base.Objects;
/**
* Palette command for creating {@link ErrorPropagation} elements.
*/
public class CreateErrorPropagationPaletteCommand extends BasePaletteCommand implements TargetedPaletteCommand {
private final DirectionType direction;
private final boolean containment;
/**
* Creates a new instance
* @param direction the direction of the error propagation to be created by this command
* @param containment if true, this command will create an error containment. If false, the command will create an error propagation.
*/
public CreateErrorPropagationPaletteCommand(final DirectionType direction, final boolean containment) {
super(StringUtil.capitalize(direction.getName()) + (containment ? " Error Containment" : " Error Propagation"),
ErrorModelPaletteCategories.ERROR_PROPAGATION, null);
this.direction = direction;
this.containment = containment;
}
@Override
public Optional<Operation> getOperation(final GetTargetedOperationContext ctx) {
final Object bo = ctx.getTarget().getBusinessObject();
if (bo instanceof Feature) {
return createPropgationCreationOperation(ctx.getTarget(), (newPropagation, subclause) -> {
// Find the feature in the context of the EMV subclause. This is needed for reliable serialization.
final List<URI> path = ErrorModelGeUtil.createQualifiedPropagationPointPath(ctx.getTarget(),
ErrorModelGeUtil.getClassifierSourceBoc(ctx.getTarget()).get(),
new ArrayList<>());
newPropagation.setFeatureorPPRef(buildFeatureReference(subclause.eResource().getResourceSet(), path));
});
} else if (bo instanceof KeywordPropagationPoint) {
final KeywordPropagationPoint kw = (KeywordPropagationPoint) bo;
if (kw.getType() != KeywordPropagationPointType.ALL) {
return createPropgationCreationOperation(ctx.getTarget(),
(newPropagation, subclause) -> newPropagation.setKind(kw.getType().getKind()));
}
} else if (bo instanceof PropagationPoint) {
return createPropgationCreationOperation(ctx.getTarget(), (newPropagation, subclause) -> {
// Find the propagation in the context of the EMV subclause.
// Check inherited subclauses as well. This is needed for reliable serialization.
final CombinedErrorModelSubclause combined = CombinedErrorModelSubclause
.create(subclause.getContainingClassifier());
final String boName = ((PropagationPoint) bo).getName();
final PropagationPoint pp = combined.getPoints().filter(p -> Objects.equal(p.getName(), boName))
.findAny().orElseThrow(() -> new AadlGraphicalEditorException("Unable to find propagation point"));
final FeatureorPPReference ppRef = ErrorModelFactory.eINSTANCE.createFeatureorPPReference();
ppRef.setFeatureorPP(pp);
newPropagation.setFeatureorPPRef(ppRef);
});
}
return Optional.empty();
}
/**
* Returns true if there is already a propagation defined for the target.
* This function only checks the first subclause of the classifier associated with the target. This is because
* propagation points may override those in other subclauses.
* @param target the target
* @return whether a matching propagation is already defined.
*/
private boolean propagationAlreadyExists(final BusinessObjectContext target) {
return ErrorModelGeUtil.getClassifier(target).map(classifier -> {
final PropagationNode propagations = new PropagationNode();
ErrorModelGeUtil.getFirstErrorModelSubclause(classifier)
.ifPresent(subclause -> subclause.getPropagations().forEach(propagations::put));
return PropagationTreeUtil.getPropagationsForBusinessObjectContext(propagations, target)
.anyMatch(p -> p.isNot() == containment && p.getDirection() == direction);
}).orElse(false);
}
/**
* Builds a {@link FeatureorPPReference} based on a name path. Throws an exception if unable to find any of the
* referenced features.
*/
private FeatureorPPReference buildFeatureReference(final ResourceSet resourceSet, final List<URI> path) {
FeatureorPPReference topPpRef = null;
FeatureorPPReference lastPpRef = null;
for (final URI pathSegmentUri : path) {
final EObject pathSegment = resourceSet.getEObject(pathSegmentUri, true);
if (!(pathSegment instanceof Feature)) {
throw new AadlGraphicalEditorException("Unexpected path segment: " + pathSegment);
}
final FeatureorPPReference ppRef = ErrorModelFactory.eINSTANCE.createFeatureorPPReference();
ppRef.setFeatureorPP((Feature) pathSegment);
if (lastPpRef == null) {
topPpRef = ppRef;
} else {
lastPpRef.setNext(ppRef);
}
lastPpRef = ppRef;
}
return topPpRef;
}
/**
* Creates an operation for creating a propagation for the target.
* @param target is the target of the propagation. It should be the context which will be the parent of the propagation
* @param init function called to finish initializing the propagation. It must set the kind or the feature or PP reference.
* @return the operation or an empty optional if a classifier could not be determined.
*/
private Optional<Operation> createPropgationCreationOperation(final BusinessObjectContext target,
final BiConsumer<ErrorPropagation, ErrorModelSubclause> init) {
return ErrorModelGeUtil.getClassifierSourceBoc(target).flatMap(container -> {
final AadlPackage pkg = container.getBusinessObject(NamedElement.class).map(ne -> ne.getElementRoot())
.map(root -> root instanceof AadlPackage ? ((AadlPackage) root) : null)
.orElseThrow(() -> new AadlGraphicalEditorException("Unable to find model"));
return ErrorModelGeUtil.createErrorModelSubclausePromptAndModifyOperation(container, () -> {
if (propagationAlreadyExists(target)) {
final String propagationOrContainmentLabel = (containment ? "containment" : "propagation");
final String inputOrOutputLabel = direction == DirectionType.IN ? "intput" : "output";
MessageDialog.openError(Display.getDefault().getActiveShell(),
"Unable to create " + propagationOrContainmentLabel,
"Propagation already exists. A propagation point may only have one " + inputOrOutputLabel
+ " error " + propagationOrContainmentLabel + " defined.");
return Optional.empty();
}
return ErrorModelUiUtil.promptForTypeSet(pkg);
}, (subclause, typeSet) -> {
final ErrorPropagation newPropagation = ErrorModelFactory.eINSTANCE.createErrorPropagation();
newPropagation.setTypeSet(typeSet);
newPropagation.setNot(containment);
newPropagation.setDirection(direction);
init.accept(newPropagation, subclause);
subclause.getPropagations().add(newPropagation);
return StepResultBuilder.create().showNewBusinessObject(target, newPropagation).build();
});
});
}
}