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

import com.google.inject.Inject
import hu.elte.txtuml.xtxtuml.xtxtUML.TUClass
import hu.elte.txtuml.xtxtuml.xtxtUML.TUComposition
import hu.elte.txtuml.xtxtuml.xtxtUML.TUConnector
import hu.elte.txtuml.xtxtuml.xtxtUML.TUConnectorEnd
import hu.elte.txtuml.xtxtuml.xtxtUML.TUPort
import hu.elte.txtuml.xtxtuml.xtxtUML.TUState
import hu.elte.txtuml.xtxtuml.xtxtUML.TUTransition
import hu.elte.txtuml.xtxtuml.xtxtUML.TUTransitionPort
import hu.elte.txtuml.xtxtuml.xtxtUML.XtxtUMLPackage
import org.eclipse.xtext.EcoreUtil2
import org.eclipse.xtext.naming.IQualifiedNameProvider
import org.eclipse.xtext.validation.Check

class XtxtUMLPortValidator extends XtxtUMLAssociationValidator {

	public static val CONNECTOR_ASSEMBLY_CONTAINS_CONTAINER_ROLE = "hu.elte.txtuml.xtxtuml.issues.ConnectorAssemblyContainsContainerRole";
	public static val CONNECTOR_CONTAINER_ROLE_COUNT_MISMATCH = "hu.elte.txtuml.xtxtuml.issues.ConnectorContainerRoleCountMismatch";
	public static val CONNECTOR_INCOMPATIBLE_PORTS = "hu.elte.txtuml.xtxtuml.ConnectorIncompatiblePorts";
	public static val CONNECTOR_ROLE_COMPOSITION_MISMATCH = "hu.elte.txtuml.xtxtuml.issues.ConnectorRoleCompositionMismatch";

	public static val CONNECTOR_END_COUNT_MISMATCH = "hu.elte.txtuml.xtxtuml.issues.ConnectorEndCountMismatch";
	public static val CONNECTOR_END_DUPLICATE = "hu.elte.txtuml.xtxtuml.issues.ConnectorEndDuplicate";
	public static val CONNECTOR_END_PORT_OWNER_MISMATCH = "hu.elte.txtuml.xtxtuml.issues.ConnectorEndPortOwnerMismatch";

	public static val PORT_INTERFACE_COUNT_MISMATCH = "hu.elte.txtuml.xtxtuml.issues.PortInterfaceCountMismatch";
	public static val PORT_NAME_IS_NOT_UNIQUE = "hu.elte.txtuml.xtxtuml.issues.PortNameIsNotUnique";

	public static val TRIGGER_PORT_IS_NOT_BEHAVIOR = "hu.elte.txtuml.xtxtuml.issues.TriggerPortIsNotBehavior";
	public static val TRIGGER_PORT_OWNER_MISMATCH = "hu.elte.txtuml.xtxtuml.issues.TriggerPortOwnerMismatch";

	@Inject extension IQualifiedNameProvider

	@Check
	def checkPortHaveAtMostOneInterfacePerType(TUPort port) {
		if (port.members.filter[required].length > 1 || port.members.filter[!required].length > 1) {
			error("Port " + port.name + " must not specify more than one required or provided interface", port,
				XtxtUMLPackage::eINSTANCE.TUClassProperty_Name, PORT_INTERFACE_COUNT_MISMATCH);
		}
	}

	@Check
	def checkConnectorHaveExactlyTwoEnds(TUConnector connector) {
		val endCount = connector.ends.length;
		if (endCount != 2) {
			error("Connector " + connector.name + " must have exactly two ends", connector,
				XtxtUMLPackage::eINSTANCE.TUModelElement_Name, CONNECTOR_END_COUNT_MISMATCH, endCount.toString);
		}
	}

	@Check
	def checkContainerEndIsAllowedAndNeededOnlyInDelegation(TUConnector connector) {
		val containerEnds = connector.ends.filter[role.isContainer];
		if (connector.delegation) {
			if (1 != containerEnds.length) {
				error("Delegation connector " + connector.name + " must have exactly one container role", connector,
					XtxtUMLPackage.eINSTANCE.TUModelElement_Name, CONNECTOR_CONTAINER_ROLE_COUNT_MISMATCH,
					connector.name);
			}
		} else {
			containerEnds.forEach [
				error("Container role " + it.role.name + " of connector end " + it.name +
					" must not be present in an assembly connector", it, XtxtUMLPackage.eINSTANCE.TUConnectorEnd_Role,
					CONNECTOR_ASSEMBLY_CONTAINS_CONTAINER_ROLE, it.role.name);
			]
		}
	}

	@Check
	def checkCompositionsBehindConnectorEnds(TUConnector connector) {
		if (connector.ends.size != 2) {
			return;
		}

		val roleA = connector.ends.get(0).role;
		val roleB = connector.ends.get(1).role;

		val containerOfRoleA = if (roleA.eContainer instanceof TUComposition) roleA.eContainer as TUComposition;
		val containerOfRoleB = if (roleB.eContainer instanceof TUComposition) roleB.eContainer as TUComposition;

		if (connector.delegation) {
			if (containerOfRoleA == null || containerOfRoleA.fullyQualifiedName != containerOfRoleB?.fullyQualifiedName) {
				error("Delegation connector " + connector.name + " must connect ports of a component and one of its parts",
					connector, XtxtUMLPackage::eINSTANCE.TUModelElement_Name,
					CONNECTOR_ROLE_COMPOSITION_MISMATCH, connector.name);
			}
		} else { // assembly connector
			if (containerOfRoleA == null || containerOfRoleB == null // roles must be from compositions
			|| containerOfRoleA.fullyQualifiedName == containerOfRoleB.fullyQualifiedName // underlying compositions must be different
			|| containerOfRoleA.ends.findFirst[container]?.endClass?.fullyQualifiedName !=
				containerOfRoleB.ends.findFirst[container]?.endClass?.fullyQualifiedName // container must be the same
			) {
				error("Assembly connector " + connector.name + " must connect ports of parts belonging to the same component",
					connector, XtxtUMLPackage::eINSTANCE.TUModelElement_Name,
					CONNECTOR_ROLE_COMPOSITION_MISMATCH, connector.name);
			}
		}
	}

	@Check
	def checkConnectorEndPortCompatibility(TUConnector connector) {
		if (connector.ends.size != 2) {
			return;
		}

		val portA = connector.ends.get(0).port;
		val portB = connector.ends.get(1).port;

		val requiredAName = portA.getInterface(true)?.fullyQualifiedName;
		val requiredBName = portB.getInterface(true)?.fullyQualifiedName;
		val providedAName = portA.getInterface(false)?.fullyQualifiedName;
		val providedBName = portB.getInterface(false)?.fullyQualifiedName;

		if (connector.delegation && (requiredAName != requiredBName || providedAName != providedBName)
		|| !connector.delegation && (requiredAName != providedBName || providedAName != requiredBName)
		) {
			error("Connector " + connector.name + " connects incompatible ports", connector,
				XtxtUMLPackage::eINSTANCE.TUModelElement_Name, CONNECTOR_INCOMPATIBLE_PORTS, connector.name);
		}
	}

	def private getInterface(TUPort port, boolean ofTypeReqiured) {
		port.members.findFirst[required == ofTypeReqiured]?.interface
	}

	@Check
	def checkOwnerOfConnectorEndPort(TUConnectorEnd connEnd) {
		val ownerOfPort = connEnd.port.eContainer;
		val classInRole = connEnd.role.endClass;

		if (ownerOfPort.fullyQualifiedName != classInRole.fullyQualifiedName) {
			error(connEnd.port.name + " cannot be resolved as a port of class " + classInRole.name, connEnd,
				XtxtUMLPackage::eINSTANCE.TUConnectorEnd_Port, CONNECTOR_END_PORT_OWNER_MISMATCH, connEnd.port.name);
		}
	}

	@Check<commit_msg>Fixes typo.<commit_after>package hu.elte.txtuml.xtxtuml.validation

import com.google.inject.Inject
import hu.elte.txtuml.xtxtuml.xtxtUML.TUClass
import hu.elte.txtuml.xtxtuml.xtxtUML.TUComposition
import hu.elte.txtuml.xtxtuml.xtxtUML.TUConnector
import hu.elte.txtuml.xtxtuml.xtxtUML.TUConnectorEnd
import hu.elte.txtuml.xtxtuml.xtxtUML.TUPort
import hu.elte.txtuml.xtxtuml.xtxtUML.TUState
import hu.elte.txtuml.xtxtuml.xtxtUML.TUTransition
import hu.elte.txtuml.xtxtuml.xtxtUML.TUTransitionPort
import hu.elte.txtuml.xtxtuml.xtxtUML.XtxtUMLPackage
import org.eclipse.xtext.EcoreUtil2
import org.eclipse.xtext.naming.IQualifiedNameProvider
import org.eclipse.xtext.validation.Check

class XtxtUMLPortValidator extends XtxtUMLAssociationValidator {

	public static val CONNECTOR_ASSEMBLY_CONTAINS_CONTAINER_ROLE = "hu.elte.txtuml.xtxtuml.issues.ConnectorAssemblyContainsContainerRole";
	public static val CONNECTOR_CONTAINER_ROLE_COUNT_MISMATCH = "hu.elte.txtuml.xtxtuml.issues.ConnectorContainerRoleCountMismatch";
	public static val CONNECTOR_INCOMPATIBLE_PORTS = "hu.elte.txtuml.xtxtuml.ConnectorIncompatiblePorts";
	public static val CONNECTOR_ROLE_COMPOSITION_MISMATCH = "hu.elte.txtuml.xtxtuml.issues.ConnectorRoleCompositionMismatch";

	public static val CONNECTOR_END_COUNT_MISMATCH = "hu.elte.txtuml.xtxtuml.issues.ConnectorEndCountMismatch";
	public static val CONNECTOR_END_DUPLICATE = "hu.elte.txtuml.xtxtuml.issues.ConnectorEndDuplicate";
	public static val CONNECTOR_END_PORT_OWNER_MISMATCH = "hu.elte.txtuml.xtxtuml.issues.ConnectorEndPortOwnerMismatch";

	public static val PORT_INTERFACE_COUNT_MISMATCH = "hu.elte.txtuml.xtxtuml.issues.PortInterfaceCountMismatch";
	public static val PORT_NAME_IS_NOT_UNIQUE = "hu.elte.txtuml.xtxtuml.issues.PortNameIsNotUnique";

	public static val TRIGGER_PORT_IS_NOT_BEHAVIOR = "hu.elte.txtuml.xtxtuml.issues.TriggerPortIsNotBehavior";
	public static val TRIGGER_PORT_OWNER_MISMATCH = "hu.elte.txtuml.xtxtuml.issues.TriggerPortOwnerMismatch";

	@Inject extension IQualifiedNameProvider

	@Check
	def checkPortHaveAtMostOneInterfacePerType(TUPort port) {
		if (port.members.filter[required].length > 1 || port.members.filter[!required].length > 1) {
			error("Port " + port.name + " must not specify more than one required or provided interface", port,
				XtxtUMLPackage::eINSTANCE.TUClassProperty_Name, PORT_INTERFACE_COUNT_MISMATCH);
		}
	}

	@Check
	def checkConnectorHaveExactlyTwoEnds(TUConnector connector) {
		val endCount = connector.ends.length;
		if (endCount != 2) {
			error("Connector " + connector.name + " must have exactly two ends", connector,
				XtxtUMLPackage::eINSTANCE.TUModelElement_Name, CONNECTOR_END_COUNT_MISMATCH, endCount.toString);
		}
	}

	@Check
	def checkContainerEndIsAllowedAndNeededOnlyInDelegation(TUConnector connector) {
		val containerEnds = connector.ends.filter[role.isContainer];
		if (connector.delegation) {
			if (1 != containerEnds.length) {
				error("Delegation connector " + connector.name + " must have exactly one container role", connector,
					XtxtUMLPackage.eINSTANCE.TUModelElement_Name, CONNECTOR_CONTAINER_ROLE_COUNT_MISMATCH,
					connector.name);
			}
		} else {
			containerEnds.forEach [
				error("Container role " + it.role.name + " of connector end " + it.name +
					" must not be present in an assembly connector", it, XtxtUMLPackage.eINSTANCE.TUConnectorEnd_Role,
					CONNECTOR_ASSEMBLY_CONTAINS_CONTAINER_ROLE, it.role.name);
			]
		}
	}

	@Check
	def checkCompositionsBehindConnectorEnds(TUConnector connector) {
		if (connector.ends.size != 2) {
			return;
		}

		val roleA = connector.ends.get(0).role;
		val roleB = connector.ends.get(1).role;

		val containerOfRoleA = if (roleA.eContainer instanceof TUComposition) roleA.eContainer as TUComposition;
		val containerOfRoleB = if (roleB.eContainer instanceof TUComposition) roleB.eContainer as TUComposition;

		if (connector.delegation) {
			if (containerOfRoleA == null || containerOfRoleA.fullyQualifiedName != containerOfRoleB?.fullyQualifiedName) {
				error("Delegation connector " + connector.name + " must connect ports of a component and one of its parts",
					connector, XtxtUMLPackage::eINSTANCE.TUModelElement_Name,
					CONNECTOR_ROLE_COMPOSITION_MISMATCH, connector.name);
			}
		} else { // assembly connector
			if (containerOfRoleA == null || containerOfRoleB == null // roles must be from compositions
			|| containerOfRoleA.fullyQualifiedName == containerOfRoleB.fullyQualifiedName // underlying compositions must be different
			|| containerOfRoleA.ends.findFirst[container]?.endClass?.fullyQualifiedName !=
				containerOfRoleB.ends.findFirst[container]?.endClass?.fullyQualifiedName // container must be the same
			) {
				error("Assembly connector " + connector.name + " must connect ports of parts belonging to the same component",
					connector, XtxtUMLPackage::eINSTANCE.TUModelElement_Name,
					CONNECTOR_ROLE_COMPOSITION_MISMATCH, connector.name);
			}
		}
	}

	@Check
	def checkConnectorEndPortCompatibility(TUConnector connector) {
		if (connector.ends.size != 2) {
			return;
		}

		val portA = connector.ends.get(0).port;
		val portB = connector.ends.get(1).port;

		val requiredAName = portA.getInterface(true)?.fullyQualifiedName;
		val requiredBName = portB.getInterface(true)?.fullyQualifiedName;
		val providedAName = portA.getInterface(false)?.fullyQualifiedName;
		val providedBName = portB.getInterface(false)?.fullyQualifiedName;

		if (connector.delegation && (requiredAName != requiredBName || providedAName != providedBName)
		|| !connector.delegation && (requiredAName != providedBName || providedAName != requiredBName)
		) {
			error("Connector " + connector.name + " connects incompatible ports", connector,
				XtxtUMLPackage::eINSTANCE.TUModelElement_Name, CONNECTOR_INCOMPATIBLE_PORTS, connector.name);
		}
	}

	def private getInterface(TUPort port, boolean ofTypeRequired) {
		port.members.findFirst[required == ofTypeRequired]?.interface
	}

	@Check
	def checkOwnerOfConnectorEndPort(TUConnectorEnd connEnd) {
		val ownerOfPort = connEnd.port.eContainer;
		val classInRole = connEnd.role.endClass;

		if (ownerOfPort.fullyQualifiedName != classInRole.fullyQualifiedName) {
			error(connEnd.port.name + " cannot be resolved as a port of class " + classInRole.name, connEnd,
				XtxtUMLPackage::eINSTANCE.TUConnectorEnd_Port, CONNECTOR_END_PORT_OWNER_MISMATCH, connEnd.port.name);
		}
	}

	@Check
{'fraction_non_alphanumeric': 0.09430881666925828, 'fraction_numerical': 0.001710464935468823, 'mean_word_length': 5.586277521761393, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 7, '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': '145720', 'n_tokens_mistral': 4702, 'n_tokens_neox': 4311, 'n_words': 730}