Aadl2Visitors.java

/**
 * AADL-Utils
 *
 * Copyright © 2012 TELECOM ParisTech and CNRS
 *
 * TELECOM ParisTech/LTCI
 *
 * Authors: see AUTHORS
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the Eclipse Public License as published by Eclipse,
 * either version 2.0 of the License, or (at your option) any later version.
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * Eclipse Public License for more details.
 * You should have received a copy of the Eclipse Public License
 * along with this program.  If not, see
 * https://www.eclipse.org/legal/epl-2.0/
 */

package org.osate.utils.internal;

import java.util.LinkedHashSet;
import java.util.Set;

import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.xtext.linking.lazy.LazyLinkingResource;
import org.osate.aadl2.Aadl2Package;
import org.osate.aadl2.Classifier;
import org.osate.aadl2.ComponentImplementation;
import org.osate.aadl2.Element;
import org.osate.aadl2.Feature;
import org.osate.aadl2.NamedElement;
import org.osate.aadl2.Namespace;
import org.osate.aadl2.PackageSection;
import org.osate.aadl2.PropertySet;
import org.osate.aadl2.Prototype;
import org.osate.aadl2.PrototypeBinding;
import org.osate.aadl2.Subcomponent;
import org.osate.xtext.aadl2.properties.linking.PropertiesLinkingService;

/**
 * A collection of AADL2 visitors.
 * @since 2.0
 */
public class Aadl2Visitors {
	/**
	 * Get all members, inherit members included, of a given name space.
	 * Member order is kept. Using LinkedHashSet avoids duplicated members
	 * introduced by inheritance.
	 *
	 * @param ns the given name space
	 * @return the component's members LinkedHashSet
	 */
	public static LinkedHashSet<NamedElement> getMembers(Namespace ns) {
		LinkedHashSet<NamedElement> result = new LinkedHashSet<NamedElement>();
		result.addAll(ns.getMembers());
		return result;
	}

	/**
	 * Return the package section related to a given Element object.
	 *
	 * @param element the given Element object
	 * @return the package section related to the given Element object
	 */
	public static PackageSection getContainingPackageSection(Element element) {
		Element container = element.getOwner();
		while (container != null && !(container instanceof PackageSection)) {
			container = container.getOwner();
		}
		return (PackageSection) container;
	}

	/**
	 * Returns the list of members within a given name space (inherit members
	 * included) witch type matches with the specified one. If no members is
	 * found, the returned list is a empty list. The members order is kept.
	 * The returned list is free from duplicated members (due to inheritance).
	 *
	 * @param <T> the specified type
	 * @param ns the given name space
	 * @param klass the specified type's class object
	 * @return the members list
	 */
	@SuppressWarnings("unchecked")
	public static <T> EList<T> getElementsInNamespace(Namespace ns, Class<T> klass) {
		Set<NamedElement> lne = getMembers(ns);
		EList<T> result = new BasicEList<T>(lne.size());
		for (NamedElement ne : lne) {
			if (klass.isAssignableFrom(ne.getClass())) {
				result.add((T) ne);
			}
		}
		return result;
	}

	/**
	 * Find the first occurrence of a Subcomponent within a given aadls'
	 * component (and ancestors) which name equals to a given name. Return
	 * {@code null} if no Subcomponent is found.
	 *
	 * @param cc the given aadl's component
	 * @param subComponentName the given name
	 * @return the first occurrence of a Subcomponent related to the given name
	 * or {@code null}
	 */
	public static Subcomponent findSubcomponentInComponent(Classifier cc, String subComponentName) {
		Subcomponent result = null;

		if (cc instanceof ComponentImplementation) {
			EList<Subcomponent> lsubcs = ((ComponentImplementation) cc).getAllSubcomponents();
			for (Subcomponent subc : lsubcs) {
				if (subc.getName().equalsIgnoreCase(subComponentName)) {
					result = subc;
					break;
				}
			}
		}

		return result;
	}

	/**
	 * Find the first occurrence of an Feature within a given aadl's component
	 * (and ancestors) which name equals to a given name. Return {@code null} if
	 * no Feature is found.
	 *
	 * @param cc the given aadl's component
	 * @param featureName the given name
	 * @return the first occurrence of a Feature related to the given name or
	 * {@code null}
	 */
	public static Feature findFeatureInComponent(Classifier cc, String featureName) {
		for (Feature f : cc.getAllFeatures()) {
			if (featureName.equalsIgnoreCase(f.getName())) {
				return f;
			}
		}
		return null;
	}

	/**
	 * Returns the first occurrence of a Prototype within the given
	 * component and its ancestors which name equals to a given name or
	 * returns {@code null} if there isn't matching prototype found.
	 *
	 * @param c the given aadl's component
	 * @param prototypeName the given name
	 * @return the first occurrence of a Prototype object related to the given
	 * name or {@code null}
	 */
	public static Prototype findPrototypeInComponent(Classifier c, String prototypeName) {
		Prototype result = null;

		EList<Prototype> lprotos = getElementsInNamespace(c, Prototype.class);

		for (Prototype proto : lprotos) {
			if (proto.getName().equalsIgnoreCase(prototypeName)) {
				result = proto;
				break;
			}
		}

		return result;
	}

	/**
	 * Returns the first occurrence of a PrototypeBinding object within the given
	 * component or component implementation and its ancestors which concerns
	 * a prototype which name equals to the given one or {@code null}
	 * if there isn't matching PrototypeBinding object found.
	 * @param c the given component or component implementation
	 * @param prototypeName the prototype name
	 * @return the first occurrence of a PrototypeBinding object or {@code null}
	 */
	public static PrototypeBinding findPrototypeBindingInComponent(Classifier c, String prototypeName) {
		if (c == null) {
			return null;
		}

		PrototypeBinding result = null;

		for (PrototypeBinding pb : c.getOwnedPrototypeBindings()) {
			if (pb.getFormal() != null && prototypeName.equalsIgnoreCase(pb.getFormal().getName())) {
				return pb;
			}
		}

		// Recursive call for component implementation.
		if (result == null && c instanceof ComponentImplementation) {
			ComponentImplementation ci = (ComponentImplementation) c;

			result = findPrototypeBindingInComponent(ci.getType(), prototypeName);
		}

		// Recursive call for parent component.
		if (result == null && c.getExtended() != null) {
			result = findPrototypeBindingInComponent(c.getExtended(), prototypeName);
		}

		return result;
	}

	/**
	 * Fetch the service associated to the given package section.
	 * If the package section's resource is a lazy load resource, it gives the
	 * resource's service.
	 *
	 * @param context the given package section
	 * @return the service associated to the given package section
	 */
	public static PropertiesLinkingService getPropertiesLinkingService(PackageSection context) {
		if (context.eResource() instanceof LazyLinkingResource) {
			return (PropertiesLinkingService) ((LazyLinkingResource) context.eResource()).getLinkingService();
		} else {
			return new PropertiesLinkingService();
		}
	}

	/**
	 * Find an element in a package based on their name from the given point of
	 * view (package section) or return {@code null}.
	 *
	 * @param elementName the element's name
	 * @param packageName the package's name
	 * @param context the given point of view
	 * @return the element or {@code null}
	 */
	public static NamedElement findElementInPackage(String elementName, String packageName, PackageSection context) {
		NamedElement result = null;

		NamedElement rootContainer = context.getElementRoot();

		String currentNamespace = rootContainer.getName();

		PropertiesLinkingService pls = Aadl2Visitors.getPropertiesLinkingService(context);

		if (packageName == null || currentNamespace == null || currentNamespace.equalsIgnoreCase(packageName)) {
			// The element is declared into the current context.
			result = pls.findNamedElementInsideAadlPackage(elementName, context);
		} else
		// The element is defined into an imported package.
		{
			result = pls.findNamedElementInAadlPackage(packageName, elementName, context);
		}

		return result;
	}

	/**
	 * Find a property in a propertyset based on their name from the given point of
	 * view (package section) or return {@code null}.
	 *
	 * @param elementName the element's name
	 * @param propertySetName the propertyset's name
	 * @param context the given point of view
	 * @return the property or {@code null}
	 */
	public static NamedElement findElementInPropertySet(String elementName, String propertySetName,
			PackageSection context) {
		NamedElement result = null;

		PropertiesLinkingService pls = Aadl2Visitors.getPropertiesLinkingService(context);

		// First in the predeclared propertysets.

		// Why this, i don't know ...
		EReference reference = Aadl2Package.eINSTANCE.getNamedValue_NamedValue();

		result = (NamedElement) pls.findNamedElementInPredeclaredPropertySets(elementName, context, reference);
		// Then in the imported property sets.
		if (result == null) {
			PropertySet ps = null;

			ps = pls.findPropertySet(context, propertySetName);

			if (ps != null) {
				result = ps.findNamedElement(elementName);
			}
		}

		return result;
	}
}