<commit_before>package hu.elte.txtuml.xtxtuml.typesystem

import com.google.inject.Inject
import hu.elte.txtuml.api.model.Collection
import hu.elte.txtuml.api.model.Signal
import hu.elte.txtuml.xtxtuml.xtxtUML.RAlfDeleteObjectExpression
import hu.elte.txtuml.xtxtuml.xtxtUML.RAlfSendSignalExpression
import hu.elte.txtuml.xtxtuml.xtxtUML.RAlfSignalAccessExpression
import hu.elte.txtuml.xtxtuml.xtxtUML.TUAssociationEnd
import hu.elte.txtuml.xtxtuml.xtxtUML.TUClass
import hu.elte.txtuml.xtxtuml.xtxtUML.TUClassPropertyAccessExpression
import hu.elte.txtuml.xtxtuml.xtxtUML.TUEntryOrExitActivity
import hu.elte.txtuml.xtxtuml.xtxtUML.TUPort
import hu.elte.txtuml.xtxtuml.xtxtUML.TUState
import hu.elte.txtuml.xtxtuml.xtxtUML.TUStateType
import hu.elte.txtuml.xtxtuml.xtxtUML.TUTransition
import hu.elte.txtuml.xtxtuml.xtxtUML.TUTransitionTrigger
import hu.elte.txtuml.xtxtuml.xtxtUML.TUTransitionVertex
import java.util.ArrayList
import java.util.HashSet
import org.eclipse.xtext.common.types.JvmType
import org.eclipse.xtext.xbase.XBlockExpression
import org.eclipse.xtext.xbase.XVariableDeclaration
import org.eclipse.xtext.xbase.annotations.typesystem.XbaseWithAnnotationsTypeComputer
import org.eclipse.xtext.xbase.jvmmodel.IJvmModelAssociations
import org.eclipse.xtext.xbase.typesystem.computation.ITypeComputationState
import org.eclipse.xtext.xbase.typesystem.conformance.ConformanceFlags
import org.eclipse.xtext.xbase.typesystem.references.LightweightTypeReference
import org.eclipse.xtext.xbase.typesystem.references.ParameterizedTypeReference

class XtxtUMLTypeComputer extends XbaseWithAnnotationsTypeComputer {

	@Inject extension IJvmModelAssociations;

	def dispatch void computeTypes(TUClassPropertyAccessExpression accessExpr, ITypeComputationState state) {
		// expectations for childs are enabled by default,
		// see val foo = if (bar) foobar else baz
		val childState = state.withoutRootExpectation;

		// left child
		childState.computeTypes(accessExpr.left);

		// right child
		val rightChild = accessExpr.right;
		// if the reference couldn't be resolved
		if (rightChild?.name == null) {
			return;
		}

		switch (rightChild) {
			TUAssociationEnd: {
				val accocEndTypeRef = state.referenceOwner.toLightweightTypeReference(
					rightChild.endClass.getPrimaryJvmElement as JvmType);

				val collectionOfAssocEndTypeRef = getTypeForName(Collection, state).
					rawTypeReference as ParameterizedTypeReference;
				collectionOfAssocEndTypeRef.addTypeArgument(accocEndTypeRef);

				state.acceptActualType(collectionOfAssocEndTypeRef);
			}
			TUPort: {
				val portTypeRef = state.referenceOwner.toLightweightTypeReference(
					rightChild.getPrimaryJvmElement as JvmType);

				state.acceptActualType(portTypeRef);
			}
		}
	}

	def dispatch computeTypes(RAlfSignalAccessExpression sigExpr, ITypeComputationState state) {
		var container = sigExpr.eContainer;
		while (container != null && !(container instanceof TUEntryOrExitActivity) &&
			!(container instanceof TUTransition)) {
			container = container.eContainer;
		}

		var visitedStates = new HashSet<TUState>();
		var type = switch (container) {
			TUEntryOrExitActivity:
				if (container.eContainer instanceof TUState) {
					getCommonSignalSuperType(
						container.eContainer as TUState,
						state,
						container.entry,
						visitedStates
					)
				} else {
					getTypeForName(Signal, state)
				}
			TUTransition:
				getCommonSignalSuperType(container, state, visitedStates)
			default:
				getTypeForName(Signal, state)
		}

		state.acceptActualType(type);
	}

	def private LightweightTypeReference getCommonSignalSuperType(
		TUTransition trans,
		ITypeComputationState cState,
		HashSet<TUState> visitedStates
	) {
		var trigger = (trans.members.findFirst [
			it instanceof TUTransitionTrigger
		] as TUTransitionTrigger)?.trigger;

		if (trigger != null) {
			val jvmSignal = trigger.getPrimaryJvmElement as JvmType;
			return if (jvmSignal != null) {
				cState.referenceOwner.toLightweightTypeReference(jvmSignal)
			} else {
				getTypeForName(Signal, cState)
			}
		}<commit_msg>Fix type computations involving JVM model associations<commit_after>package hu.elte.txtuml.xtxtuml.typesystem

import com.google.inject.Inject
import hu.elte.txtuml.api.model.Collection
import hu.elte.txtuml.api.model.ModelClass
import hu.elte.txtuml.api.model.Port
import hu.elte.txtuml.api.model.Signal
import hu.elte.txtuml.xtxtuml.xtxtUML.RAlfDeleteObjectExpression
import hu.elte.txtuml.xtxtuml.xtxtUML.RAlfSendSignalExpression
import hu.elte.txtuml.xtxtuml.xtxtUML.RAlfSignalAccessExpression
import hu.elte.txtuml.xtxtuml.xtxtUML.TUAssociationEnd
import hu.elte.txtuml.xtxtuml.xtxtUML.TUClass
import hu.elte.txtuml.xtxtuml.xtxtUML.TUClassPropertyAccessExpression
import hu.elte.txtuml.xtxtuml.xtxtUML.TUEntryOrExitActivity
import hu.elte.txtuml.xtxtuml.xtxtUML.TUPort
import hu.elte.txtuml.xtxtuml.xtxtUML.TUState
import hu.elte.txtuml.xtxtuml.xtxtUML.TUStateType
import hu.elte.txtuml.xtxtuml.xtxtUML.TUTransition
import hu.elte.txtuml.xtxtuml.xtxtUML.TUTransitionTrigger
import hu.elte.txtuml.xtxtuml.xtxtUML.TUTransitionVertex
import java.util.ArrayList
import java.util.HashSet
import org.eclipse.emf.common.util.EList
import org.eclipse.emf.ecore.EObject
import org.eclipse.emf.ecore.resource.Resource
import org.eclipse.xtext.common.types.JvmGenericType
import org.eclipse.xtext.common.types.JvmType
import org.eclipse.xtext.naming.IQualifiedNameProvider
import org.eclipse.xtext.xbase.XBlockExpression
import org.eclipse.xtext.xbase.XVariableDeclaration
import org.eclipse.xtext.xbase.annotations.typesystem.XbaseWithAnnotationsTypeComputer
import org.eclipse.xtext.xbase.jvmmodel.IJvmModelAssociations
import org.eclipse.xtext.xbase.resource.BatchLinkableResource
import org.eclipse.xtext.xbase.typesystem.computation.ITypeComputationState
import org.eclipse.xtext.xbase.typesystem.conformance.ConformanceFlags
import org.eclipse.xtext.xbase.typesystem.references.LightweightTypeReference
import org.eclipse.xtext.xbase.typesystem.references.ParameterizedTypeReference

class XtxtUMLTypeComputer extends XbaseWithAnnotationsTypeComputer {

	@Inject extension IJvmModelAssociations;
	@Inject extension IQualifiedNameProvider;

	def dispatch void computeTypes(TUClassPropertyAccessExpression accessExpr, ITypeComputationState state) {
		// expectations for childs are enabled by default,
		// see val foo = if (bar) foobar else baz
		val childState = state.withoutRootExpectation;

		// left child
		childState.computeTypes(accessExpr.left);

		// right child
		val rightChild = accessExpr.right;
		// if the reference couldn't be resolved
		if (rightChild?.name == null) {
			return;
		}

		switch (rightChild) {
			TUAssociationEnd: {
				val collectionOfAssocEndTypeRef = getTypeForName(Collection, state).
					rawTypeReference as ParameterizedTypeReference;
				collectionOfAssocEndTypeRef.addTypeArgument(
					state.nullSafeJvmElementTypeRef(rightChild.endClass, ModelClass));

				state.acceptActualType(collectionOfAssocEndTypeRef);
			}
			TUPort: {
				state.acceptActualType(state.nullSafeJvmElementTypeRef(rightChild, Port));
			}
		}
	}

	def private nullSafeJvmElementTypeRef(ITypeComputationState state, EObject sourceElement, Class<?> fallbackType) {
		val inferredType = sourceElement.getPrimaryJvmElement as JvmType;
		return if (inferredType != null) {
			state.referenceOwner.toLightweightTypeReference(inferredType)
		} else {
			val newEquivalent = sourceElement.findNewJvmEquivalent(state.referenceOwner.contextResourceSet.resources);
			if (newEquivalent != null) {
				state.referenceOwner.toLightweightTypeReference(newEquivalent)
			} else {
				getTypeForName(fallbackType, state)
			}
		}
	}

	/**
	 * Returns a <i>JvmType</i> equivalent of <code>sourceElement</code> from the given <code>resourceSet</code>,
	 * or <code>null</code>, if no equivalent could be found. The call of this method might be expensive.
	 */
	def private findNewJvmEquivalent(EObject sourceElement, EList<Resource> resourceSet) {
		for (resource : resourceSet) {
			if (resource instanceof BatchLinkableResource) {
				val newEquivalent = resource.allContents.findFirst [
					it instanceof JvmGenericType && it.fullyQualifiedName == sourceElement.fullyQualifiedName
				];
				if (newEquivalent != null) {
					return newEquivalent as JvmGenericType;
				}
			}
		}

		return null;
	}

	def dispatch computeTypes(RAlfSignalAccessExpression sigExpr, ITypeComputationState state) {
		var container = sigExpr.eContainer;
		while (container != null && !(container instanceof TUEntryOrExitActivity) &&
			!(container instanceof TUTransition)) {
			container = container.eContainer;
		}

		var visitedStates = new HashSet<TUState>();
		var type = switch (container) {
			TUEntryOrExitActivity:
				if (container.eContainer instanceof TUState) {
					getCommonSignalSuperType(
						container.eContainer as TUState,
						state,
						container.entry,
						visitedStates
					)
				} else {
					getTypeForName(Signal, state)
				}
			TUTransition:
				getCommonSignalSuperType(container, state, visitedStates)
			default:
				getTypeForName(Signal, state)
		}

		state.acceptActualType(type);
	}

	def private LightweightTypeReference getCommonSignalSuperType(
		TUTransition trans,
		ITypeComputationState cState,
		HashSet<TUState> visitedStates
	) {
		var trigger = (trans.members.findFirst [
			it instanceof TUTransitionTrigger
		] as TUTransitionTrigger)?.trigger;

		if (trigger != null) {
			return cState.nullSafeJvmElementTypeRef(trigger, Signal);
		}
{'fraction_non_alphanumeric': 0.08376032625745058, 'fraction_numerical': 0.0, 'mean_word_length': 5.138639281129653, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 17, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4446808', 'n_tokens_mistral': 3419, 'n_tokens_neox': 3053, 'n_words': 525}