Aadl2Util.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.aadl2.util;

import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.osate.aadl2.AadlBoolean;
import org.osate.aadl2.AadlInteger;
import org.osate.aadl2.AadlReal;
import org.osate.aadl2.AadlString;
import org.osate.aadl2.BehavioredImplementation;
import org.osate.aadl2.Classifier;
import org.osate.aadl2.ContainedNamedElement;
import org.osate.aadl2.ContainmentPathElement;
import org.osate.aadl2.Element;
import org.osate.aadl2.ListType;
import org.osate.aadl2.NamedElement;
import org.osate.aadl2.NumberType;
import org.osate.aadl2.Property;
import org.osate.aadl2.PropertyType;
import org.osate.aadl2.RefinableElement;
import org.osate.aadl2.SubprogramCall;
import org.osate.aadl2.UnitLiteral;
import org.osate.aadl2.instance.ModeInstance;
import org.osate.aadl2.instance.SystemOperationMode;
import org.osate.aadl2.instance.util.InstanceUtil;

public class Aadl2Util {
	/**
	 * Xtext resolver leaves unresolved proxy when reference cannot be resolved.
	 * @param eo
	 * @return
	 */
	public static boolean isNull(EObject eo) {
		return eo == null || eo.eIsProxy();
	}

	/**
	 * Xtext resolver leaves unresolved proxy when reference cannot be resolved.
	 * @param eo
	 * @return
	 */
	public static boolean isUnresolved(EObject eo) {
		return eo.eIsProxy();
	}

	/**
	 * Check to see if the Property definitions are the same
	 * The methods compensates for the possible problem that the objects are different.
	 *
	 * @param p1 Property
	 * @param p2 Property
	 * @return true if they have the same name
	 */
	public static boolean sameProperty(Property p1, Property p2) {
		String p1Name = p1.getQualifiedName();
		String p2Name = p2.getQualifiedName();
		boolean sameName = p1Name.equalsIgnoreCase(p2Name);
		return sameName;
	}

	public static String getName(NamedElement ne) {
		if (ne.hasName()) {
			return ne.getName();
		}
		return getRefinedName(ne, ne);
	}

	/**
	 * refined Elements do not have an assigned name, but they have a reference to the Element they refine.
	 * One of them is the original with an actually assigned name
	 * @param ne Element whose name we are trying to retrieve
	 * @param root The start of the search. Used to detect cycles
	 * @return
	 */
	public static String getRefinedName(NamedElement ne, NamedElement root) {
		if (ne instanceof RefinableElement) {
			RefinableElement re = (RefinableElement) ne;
			RefinableElement ref = re.getRefinedElement();
			if (ref == root) {
				return null; // terminate on cycle
			}
			if (ref != null) {
				return getRefinedName(ref, root);
			}
			// no additional reference pointer, return name
			return ne.getName();
		}
		return null;
	}

	/**
	 * Find owned named elements. In the case of a thread implementation, subprogram implementation,
	 * or abstract implementation also look up subprogram calls.
	 * @param owner Classifier in which the lookup is performed
	 * @param name name of Element to be found
	 * @return NamedElement or null
	 */
	public static NamedElement findOwnedNamedElement(Classifier owner, String name) {
		for (Element e : owner.getOwnedElements()) {
			if (!(e instanceof NamedElement)) {
				continue;
			}
			NamedElement ne = (NamedElement) e;
			String neName = Aadl2Util.getName(ne);
			if (neName != null && neName.equalsIgnoreCase(name)) {
				return ne;
			}
		}
		if (owner instanceof BehavioredImplementation) {
			BehavioredImplementation bi = (BehavioredImplementation) owner;
			for (SubprogramCall sc : bi.getSubprogramCalls()) {
				if (sc.getName() != null && sc.getName().equalsIgnoreCase(name)) {
					return sc;
				}
			}
		}

		return null;
	}

	public static String getPrintableSOMName(SystemOperationMode som) {
		if (som == null || InstanceUtil.isNoMode(som)) {
			return "";
		}
		return "In SystemMode " + som.getName();
	}

	public static String getPrintableSOMMembers(SystemOperationMode som) {
		String members = "";
		if (som == null || InstanceUtil.isNoMode(som)) {
			return "";
		}
		for (ModeInstance mode : som.getCurrentModes()) {
			if (members.isEmpty()) {
				members = mode.getComponentInstancePath();
			} else {
				members = members + ", " + mode.getComponentInstancePath();
			}
		}
		return "(" + members + ")";
	}

	public static boolean isPrintableSOMName(SystemOperationMode som) {
		return (som != null && !InstanceUtil.isNoMode(som));
	}

	public static String getPrintablePathName(ContainedNamedElement path) {
		String pathname = "";
		EList<ContainmentPathElement> pathelements = path.getContainmentPathElements();
		for (ContainmentPathElement containmentPathElement : pathelements) {
			pathname = pathname + (pathname.isEmpty() ? "" : ".") + containmentPathElement.getNamedElement().getName();
		}
		return pathname;
	}

	/**
	 * extract the item name from a qualified name, the identifier after the last ::
	 * @param qualname String Qualified name
	 * @return String item name
	 */
	public static String getItemNameWithoutQualification(String qualname) {
		final int idx = qualname.lastIndexOf("::");
		if (idx != -1) {
			return qualname.substring(idx + 2);
		}
		return qualname;
	}

	/**
	 * extract the package name of a qualified name, everything up to the last :: or null
	 * @param qualname
	 * @return String
	 */
	public static String getPackageName(String qualname) {
		final int idx = qualname.lastIndexOf("::");
		if (idx != -1) {
			return qualname.substring(0, idx);
		}
		return null;
	}

	public static boolean sameUnit(UnitLiteral l1, UnitLiteral l2) {
		String p1Name = l1.getQualifiedName();
		String p2Name = l2.getQualifiedName();
		return p1Name.equalsIgnoreCase(p2Name);
	}

	public static boolean isNoModes(SystemOperationMode som) {
		return (som == null || som.getName().equalsIgnoreCase("No Modes"));
	}

	/**
	 * Determines if two property types are equal.  This doesn't try to be very fancy because we
	 * want to use type names to compare more complicated types.  Two types are equal if
	 * <ul>
	 *   <li>They are both {@link ListType} and the {@link ListType#getElementType element types} are equal, or
	 *   <li>They are both {@link AadlBoolean}, {@link AadlString}, {@link AadlInteger}, or
	 *   {@link AadlReal} and both have {@code null} {@link NamedElement#getName() names} and equal
	 *   {@link NumberType#getUnitsType() units types} as determined by <code>==</code>, or
	 *   <li>Both types are equal as determined by the <code>==</code> operator.
	 * </ul>
	 *
	 * @since 3.0
	 */
	public static boolean arePropertyTypesEqual(final PropertyType type1, final PropertyType type2) {
		boolean equal = false;

		if (type1 instanceof ListType && type2 instanceof ListType) {
			equal = arePropertyTypesEqual(((ListType) type1).getElementType(), ((ListType) type2).getElementType());
		} else if (type1 instanceof AadlBoolean && type2 instanceof AadlBoolean) {
			equal = type1.getName() == null && type2.getName() == null;
		} else if (type1 instanceof AadlString && type2 instanceof AadlString) {
			equal = type1.getName() == null && type2.getName() == null;
		} else if (type1 instanceof AadlInteger && type2 instanceof AadlInteger) {
			final NumberType nt1 = (NumberType) type1;
			final NumberType nt2 = (NumberType) type2;
			equal = nt1.getName() == null && nt2.getName() == null && nt1.getUnitsType() == nt2.getUnitsType();
		} else if (type1 instanceof AadlReal && type2 instanceof AadlReal) {
			final NumberType nt1 = (NumberType) type1;
			final NumberType nt2 = (NumberType) type2;
			equal = nt1.getName() == null && nt2.getName() == null && nt1.getUnitsType() == nt2.getUnitsType();
		}

		return equal ? true : type1 == type2;
	}
}