ErrorModelOutlineTreeProvider.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.xtext.aadl2.errormodel.ui.outline;

import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.xtext.Keyword;
import org.eclipse.xtext.nodemodel.INode;
import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
import org.eclipse.xtext.ui.editor.outline.IOutlineNode;
import org.eclipse.xtext.ui.editor.outline.impl.AbstractOutlineNode;
import org.eclipse.xtext.ui.editor.outline.impl.BackgroundOutlineTreeProvider;
import org.eclipse.xtext.ui.editor.outline.impl.DocumentRootNode;
import org.eclipse.xtext.util.ITextRegion;
import org.osate.aadl2.DefaultAnnexLibrary;
import org.osate.aadl2.DefaultAnnexSubclause;
import org.osate.xtext.aadl2.errormodel.errorModel.CompositeState;
import org.osate.xtext.aadl2.errormodel.errorModel.EMV2Root;
import org.osate.xtext.aadl2.errormodel.errorModel.ErrorBehaviorEvent;
import org.osate.xtext.aadl2.errormodel.errorModel.ErrorBehaviorState;
import org.osate.xtext.aadl2.errormodel.errorModel.ErrorBehaviorStateMachine;
import org.osate.xtext.aadl2.errormodel.errorModel.ErrorBehaviorTransition;
import org.osate.xtext.aadl2.errormodel.errorModel.ErrorDetection;
import org.osate.xtext.aadl2.errormodel.errorModel.ErrorFlow;
import org.osate.xtext.aadl2.errormodel.errorModel.ErrorModelLibrary;
import org.osate.xtext.aadl2.errormodel.errorModel.ErrorModelSubclause;
import org.osate.xtext.aadl2.errormodel.errorModel.ErrorPropagation;
import org.osate.xtext.aadl2.errormodel.errorModel.ErrorStateToModeMapping;
import org.osate.xtext.aadl2.errormodel.errorModel.ErrorType;
import org.osate.xtext.aadl2.errormodel.errorModel.OutgoingPropagationCondition;
import org.osate.xtext.aadl2.errormodel.errorModel.PropagationPath;
import org.osate.xtext.aadl2.errormodel.errorModel.TypeMapping;
import org.osate.xtext.aadl2.errormodel.errorModel.TypeSet;
import org.osate.xtext.aadl2.errormodel.errorModel.TypeTransformation;
import org.osate.xtext.aadl2.errormodel.services.ErrorModelGrammarAccess;

import com.google.inject.Inject;

/**
 * customization of the default outline structure
 *
 */
public class ErrorModelOutlineTreeProvider extends BackgroundOutlineTreeProvider {
	@Inject
	private ErrorModelGrammarAccess grammarAccess;

	@Override
	protected void internalCreateChildren(final DocumentRootNode parentNode, final EObject modelElement) {
		if (modelElement instanceof EMV2Root) {
			final EMV2Root emv2Root = (EMV2Root) modelElement;
			if (emv2Root.getLibrary() != null) {
				createNode(parentNode, emv2Root.getLibrary());
			} else {
				emv2Root.getSubclauses().forEach(subclause -> createNode(parentNode, subclause));
			}
		} else {
			super.internalCreateChildren(parentNode, modelElement);
		}
	}

	@Override
	protected void internalCreateChildren(final IOutlineNode parentNode, final EObject modelElement) {
		if (parentNode instanceof ErrorPropagationsOutlineNode) {
			_createChildren((ErrorPropagationsOutlineNode) parentNode, (ErrorModelSubclause) modelElement);
		} else if (parentNode instanceof ComponentErrorBehaviorOutlineNode) {
			_createChildren((ComponentErrorBehaviorOutlineNode) parentNode, (ErrorModelSubclause) modelElement);
		} else if (parentNode instanceof CompositeErrorBehaviorOutlineNode) {
			_createChildren((CompositeErrorBehaviorOutlineNode) parentNode, (ErrorModelSubclause) modelElement);
		} else if (parentNode instanceof ConnectionErrorOutlineNode) {
			_createChildren((ConnectionErrorOutlineNode) parentNode, (ErrorModelSubclause) modelElement);
		} else if (parentNode instanceof PropagationPathsOutlineNode) {
			_createChildren((PropagationPathsOutlineNode) parentNode, (ErrorModelSubclause) modelElement);
		} else if (parentNode instanceof ErrorTypesOutlineNode) {
			_createChildren((ErrorTypesOutlineNode) parentNode, (ErrorModelLibrary) modelElement);
		} else {
			if (modelElement instanceof DefaultAnnexLibrary) {
				_createChildren(parentNode, (DefaultAnnexLibrary) modelElement);
			} else if (modelElement instanceof DefaultAnnexSubclause) {
				_createChildren(parentNode, (DefaultAnnexSubclause) modelElement);
			} else if (modelElement instanceof ErrorModelSubclause) {
				_createChildren(parentNode, (ErrorModelSubclause) modelElement);
			} else if (modelElement instanceof ErrorBehaviorStateMachine) {
				_createChildren(parentNode, (ErrorBehaviorStateMachine) modelElement);
			} else if (modelElement instanceof ErrorModelLibrary) {
				_createChildren(parentNode, (ErrorModelLibrary) modelElement);
			}
		}
	}

	protected void _createChildren(ErrorPropagationsOutlineNode parentNode, ErrorModelSubclause errorModelSubclause) {
		errorModelSubclause.getPropagations().forEach(propagation -> createNode(parentNode, propagation));
		errorModelSubclause.getFlows().forEach(flow -> createNode(parentNode, flow));
	}

	protected void _createChildren(ComponentErrorBehaviorOutlineNode parentNode,
			ErrorModelSubclause errorModelSubclause) {
		errorModelSubclause.getEvents().forEach(event -> createNode(parentNode, event));
		errorModelSubclause.getTransitions().forEach(transition -> createNode(parentNode, transition));
		errorModelSubclause.getOutgoingPropagationConditions().forEach(condition -> createNode(parentNode, condition));
		errorModelSubclause.getErrorDetections().forEach(detection -> createNode(parentNode, detection));
		errorModelSubclause.getErrorStateToModeMappings().forEach(mapping -> createNode(parentNode, mapping));
	}

	protected void _createChildren(CompositeErrorBehaviorOutlineNode parentNode,
			ErrorModelSubclause errorModelSubclause) {
		errorModelSubclause.getStates().forEach(state -> createNode(parentNode, state));
	}

	protected void _createChildren(ConnectionErrorOutlineNode parentNode, ErrorModelSubclause errorModelSubclause) {
		errorModelSubclause.getConnectionErrorSources().forEach(errorSource -> createNode(parentNode, errorSource));
	}

	protected void _createChildren(PropagationPathsOutlineNode parentNode, ErrorModelSubclause errorModelSubclause) {
		errorModelSubclause.getPoints().forEach(point -> createNode(parentNode, point));
		errorModelSubclause.getPaths().forEach(path -> createNode(parentNode, path));
	}

	protected void _createChildren(ErrorTypesOutlineNode parentNode, ErrorModelLibrary errorModelLibrary) {
		errorModelLibrary.getTypes().forEach(type -> createNode(parentNode, type));
		errorModelLibrary.getTypesets().forEach(typeSet -> createNode(parentNode, typeSet));
	}

	protected void _createChildren(IOutlineNode parentNode, DefaultAnnexLibrary defaultAnnexLibrary) {
		if (defaultAnnexLibrary.getParsedAnnexLibrary() instanceof ErrorModelLibrary) {
			_createChildren(parentNode, (ErrorModelLibrary) defaultAnnexLibrary.getParsedAnnexLibrary());
		}
	}

	protected void _createChildren(IOutlineNode parentNode, DefaultAnnexSubclause defaultAnnexSubclause) {
		if (defaultAnnexSubclause.getParsedAnnexSubclause() instanceof ErrorModelSubclause) {
			_createChildren(parentNode, (ErrorModelSubclause) defaultAnnexSubclause.getParsedAnnexSubclause());
		}
	}

	protected void _createChildren(IOutlineNode parentNode, ErrorModelSubclause errorModelSubclause) {
		/*
		 * Create Error Propagations, Component Error Behavior, Composite Error Behavior, Connection Error, and
		 * Propagation Paths outline nodes.
		 */
		INode errorNode = null;
		INode endPropagationsSemiNode = null;
		INode componentNode = null;
		INode endComponentSemiNode = null;
		INode compositeNode = null;
		INode endCompositeSemiNode = null;
		INode connectionNode = null;
		INode endConnectionSemiNode = null;
		INode propagationNode = null;
		INode endPathsSemiNode = null;
		for (INode node : NodeModelUtils.getNode(errorModelSubclause).getChildren()) {
			if (node.getGrammarElement() instanceof Keyword) {
				Keyword keyword = (Keyword) node.getGrammarElement();
				if (keyword == grammarAccess.getErrorModelSubclauseAccess().getErrorKeyword_5_0()
						|| keyword == grammarAccess.getEMV2SubclauseAccess().getErrorKeyword_7_0()) {
					errorNode = node;
				} else if (keyword == grammarAccess.getErrorModelSubclauseAccess().getSemicolonKeyword_5_6()
						|| keyword == grammarAccess.getEMV2SubclauseAccess().getSemicolonKeyword_7_6()) {
					endPropagationsSemiNode = node;
				} else if (keyword == grammarAccess.getErrorModelSubclauseAccess().getComponentKeyword_6_0()
						|| keyword == grammarAccess.getEMV2SubclauseAccess().getComponentKeyword_8_0()) {
					componentNode = node;
				} else if (keyword == grammarAccess.getErrorModelSubclauseAccess().getSemicolonKeyword_6_11()
						|| keyword == grammarAccess.getEMV2SubclauseAccess().getSemicolonKeyword_8_11()) {
					endComponentSemiNode = node;
				} else if (keyword == grammarAccess.getErrorModelSubclauseAccess().getCompositeKeyword_7_0()
						|| keyword == grammarAccess.getEMV2SubclauseAccess().getCompositeKeyword_9_0()) {
					compositeNode = node;
				} else if (keyword == grammarAccess.getErrorModelSubclauseAccess().getSemicolonKeyword_7_6()
						|| keyword == grammarAccess.getEMV2SubclauseAccess().getSemicolonKeyword_9_6()) {
					endCompositeSemiNode = node;
				} else if (keyword == grammarAccess.getErrorModelSubclauseAccess().getConnectionKeyword_8_0()
						|| keyword == grammarAccess.getEMV2SubclauseAccess().getConnectionKeyword_10_0()) {
					connectionNode = node;
				} else if (keyword == grammarAccess.getErrorModelSubclauseAccess().getSemicolonKeyword_8_6()
						|| keyword == grammarAccess.getEMV2SubclauseAccess().getSemicolonKeyword_10_6()) {
					endConnectionSemiNode = node;
				} else if (keyword == grammarAccess.getErrorModelSubclauseAccess().getPropagationKeyword_9_0()
						|| keyword == grammarAccess.getEMV2SubclauseAccess().getPropagationKeyword_11_0()) {
					propagationNode = node;
				} else if (keyword == grammarAccess.getErrorModelSubclauseAccess().getSemicolonKeyword_9_6()
						|| keyword == grammarAccess.getEMV2SubclauseAccess().getSemicolonKeyword_11_6()) {
					endPathsSemiNode = node;
				}
			}
		}
		if (errorNode != null && endPropagationsSemiNode != null) {
			new ErrorPropagationsOutlineNode(parentNode, errorModelSubclause, errorNode, endPropagationsSemiNode);
		}
		if (componentNode != null && endComponentSemiNode != null) {
			new ComponentErrorBehaviorOutlineNode(parentNode, errorModelSubclause, componentNode, endComponentSemiNode);
		}
		if (compositeNode != null && endCompositeSemiNode != null) {
			new CompositeErrorBehaviorOutlineNode(parentNode, errorModelSubclause, compositeNode, endCompositeSemiNode);
		}
		if (connectionNode != null && endConnectionSemiNode != null) {
			new ConnectionErrorOutlineNode(parentNode, errorModelSubclause, connectionNode, endConnectionSemiNode);
		}
		if (propagationNode != null && endPathsSemiNode != null) {
			new PropagationPathsOutlineNode(parentNode, errorModelSubclause, propagationNode, endPathsSemiNode);
		}
	}

	protected void _createChildren(IOutlineNode parentNode, ErrorModelLibrary errorModelLibrary) {
		// Create Error Types outline node.
		INode errorNode = null;
		INode endTypesSemiNode = null;
		for (INode node : NodeModelUtils.getNode(errorModelLibrary).getChildren()) {
			if (node.getGrammarElement() instanceof Keyword) {
				Keyword keyword = (Keyword) node.getGrammarElement();
				if (keyword == grammarAccess.getErrorModelLibraryAccess().getErrorKeyword_1_0()
						|| keyword == grammarAccess.getEMV2LibraryAccess().getErrorKeyword_0_1_2_0()
						|| keyword == grammarAccess.getEMV2LibraryAccess().getErrorKeyword_1_6_0()) {
					errorNode = node;
				} else if (keyword == grammarAccess.getErrorModelLibraryAccess().getSemicolonKeyword_1_8()
						|| keyword == grammarAccess.getEMV2LibraryAccess().getSemicolonKeyword_0_1_2_8()
						|| keyword == grammarAccess.getEMV2LibraryAccess().getSemicolonKeyword_1_6_8()) {
					endTypesSemiNode = node;
				}
			}
		}
		if (errorNode != null && endTypesSemiNode != null) {
			new ErrorTypesOutlineNode(parentNode, errorModelLibrary, errorNode, endTypesSemiNode);
		}

		errorModelLibrary.getBehaviors().forEach(behavior -> createNode(parentNode, behavior));
		errorModelLibrary.getMappings().forEach(mapping -> createNode(parentNode, mapping));
		errorModelLibrary.getTransformations().forEach(transformation -> createNode(parentNode, transformation));
	}

	protected void _createChildren(IOutlineNode parentNode, ErrorBehaviorStateMachine errorBehaviorStateMachine) {
		errorBehaviorStateMachine.getEvents().forEach(event -> createNode(parentNode, event));
		errorBehaviorStateMachine.getStates().forEach(state -> createNode(parentNode, state));
		errorBehaviorStateMachine.getTransitions().forEach(transition -> createNode(parentNode, transition));
	}

	@Override
	protected boolean isLeaf(final EObject modelElement) {
		return modelElement instanceof TypeSet || modelElement instanceof TypeTransformation
				|| modelElement instanceof TypeMapping || modelElement instanceof ErrorPropagation
				|| modelElement instanceof ErrorFlow || modelElement instanceof PropagationPath
				|| modelElement instanceof ErrorBehaviorEvent || modelElement instanceof ErrorBehaviorState
				|| modelElement instanceof ErrorBehaviorTransition || modelElement instanceof ErrorType
				|| modelElement instanceof OutgoingPropagationCondition || modelElement instanceof ErrorDetection
				|| modelElement instanceof ErrorStateToModeMapping || modelElement instanceof CompositeState;
	}

	private abstract class AbstractSectionOutlineNode extends AbstractOutlineNode {
		private final URI eObjectURI;
		private final ITextRegion significantRegion;

		public AbstractSectionOutlineNode(IOutlineNode parent, EObject modelElement, String text, INode startNode,
				INode endNode) {
			super(parent, getLabelProvider().getImage(AbstractSectionOutlineNode.class), text, false);
			eObjectURI = EcoreUtil.getURI(modelElement);
			significantRegion = startNode.getTextRegionWithLineInformation();
			setTextRegion(significantRegion.merge(endNode.getTextRegionWithLineInformation()));
		}

		@Override
		public ITextRegion getSignificantTextRegion() {
			return significantRegion;
		}

		@Override
		protected URI getEObjectURI() {
			return eObjectURI;
		}
	}

	private class ErrorPropagationsOutlineNode extends AbstractSectionOutlineNode {
		public ErrorPropagationsOutlineNode(IOutlineNode parent, ErrorModelSubclause errorModelSubclause,
				INode startNode, INode endNode) {
			super(parent, errorModelSubclause, "Error Propagations", startNode, endNode);
		}
	}

	private class ComponentErrorBehaviorOutlineNode extends AbstractSectionOutlineNode {
		public ComponentErrorBehaviorOutlineNode(IOutlineNode parent, ErrorModelSubclause errorModelSubclause,
				INode startNode, INode endNode) {
			super(parent, errorModelSubclause, "Component Error Behavior", startNode, endNode);
		}
	}

	private class CompositeErrorBehaviorOutlineNode extends AbstractSectionOutlineNode {
		public CompositeErrorBehaviorOutlineNode(IOutlineNode parent, ErrorModelSubclause errorModelSubclause,
				INode startNode, INode endNode) {
			super(parent, errorModelSubclause, "Composite Error Behavior", startNode, endNode);
		}
	}

	private class ConnectionErrorOutlineNode extends AbstractSectionOutlineNode {
		public ConnectionErrorOutlineNode(IOutlineNode parent, ErrorModelSubclause errorModelSubclause, INode startNode,
				INode endNode) {
			super(parent, errorModelSubclause, "Connection Error", startNode, endNode);
		}
	}

	private class PropagationPathsOutlineNode extends AbstractSectionOutlineNode {
		public PropagationPathsOutlineNode(IOutlineNode parent, ErrorModelSubclause errorModelSubclause,
				INode startNode, INode endNode) {
			super(parent, errorModelSubclause, "Propagation Paths", startNode, endNode);
		}
	}

	private class ErrorTypesOutlineNode extends AbstractSectionOutlineNode {
		public ErrorTypesOutlineNode(IOutlineNode parent, ErrorModelLibrary errorModelLibrary, INode startNode,
				INode endNode) {
			super(parent, errorModelLibrary, "Error Types", startNode, endNode);
		}
	}
}