<commit_before>/*
 * generated by Xtext 2.13.0
 */
package fr.lip6.veca.ide.validation

import org.eclipse.xtext.validation.Check
import fr.lip6.veca.ide.vecaDsl.VecaDslPackage
import fr.lip6.veca.ide.vecaDsl.Component
import fr.lip6.veca.ide.vecaDsl.IJoinPoint
import fr.lip6.veca.ide.vecaDsl.JoinPoint
import java.util.Arrays
import fr.lip6.veca.ide.vecaDsl.Binding
import fr.lip6.veca.ide.vecaDsl.BindingInformation
import fr.lip6.veca.ide.vecaDsl.EJoinPoint
import fr.lip6.veca.ide.vecaDsl.Operation
import fr.lip6.veca.ide.vecaDsl.Message
import fr.lip6.veca.ide.vecaDsl.CompositeComponent
import fr.lip6.veca.ide.vecaDsl.InternalBinding
import fr.lip6.veca.ide.vecaDsl.ExternalBinding
import fr.lip6.veca.ide.vecaDsl.NamedComponent
import fr.lip6.veca.ide.vecaDsl.Model
import fr.lip6.veca.ide.vecaDsl.IJoinPoint

/**
 * This class contains custom validation rules. 
 *
 * See https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#validation
 */
class VecaDslValidator extends AbstractVecaDslValidator {
	
public static val INVALID_NAME = 'invalidName'
public static val UNKNOWN_OPERATION = "unknownOperation"
public static val INCOMPATIBLE_OPERATIONS = "incompatibleOperations"
public static val INCORRECT_BINDING = "incorrectBinding"
public static val INCORRECT_CHILDREN = "incorrectChildren"
public static val INCORRECT_TYPES = "incorrectTypes"
public static val SELF_COMPONENT = "selfComponent"
public static val SELF_BINDING = "selfBinding"
public static val MULTIPLE_BINDINGS_SAME_ID = "multipleBindingsWithSameId"
public static val NARY_BINDING = "naryBinding"
public static val ERROR = "error"

	// component type names begin with a capital
	@Check
	def checkComponentStartsWithCapital(Component c) {
		if (!Character.isUpperCase(c.name.charAt(0))) {
			warning('Component type name should start with a capital', 
					VecaDslPackage.Literals.COMPONENT__NAME,
					INVALID_NAME,
					c.name)
		}
	}
	
	// all component types have different names
	@Check
	def checkComponentNamesAreDifferent(Model m) {
		for(Component c: m.types) {
			if(m.types.filter[it.name.equals(c.name)].size>1) {
				error(String.format("There is more than one component type named %s", c.name),
					VecaDslPackage.Literals.MODEL__TYPES,
					INCORRECT_TYPES
				)
			}
		}
	}
	
	// bindings relate compatible operations
	// two operations are compatible if:
	// [X] they have the same name
	// [X] they have the same message types
	// [X] for internal bindings connect required to provided (in Component using checkCompatibleOperationsInComponentBindings)
	// [X] for external bindings connect self.provided to provided or required to self.required (in Component using checkCompatibleOperationsInComponentBindings)
 	@Check
	def checkCompatibleOperationsInBinding(BindingInformation b) {
		val o1 = b.point1.findOperation()
		val o2 = b.point2.findOperation()
		if (o1 !== null && o2 !== null) {
			if (!compatible(o1,o2)) {
				error("Incompatible operations (names and/or messages)",
					VecaDslPackage.Literals.BINDING_INFORMATION__POINT1,
					INCOMPATIBLE_OPERATIONS
				)
			}
		}
	}
	
	// bindings in a composite component have different identifiers
	@Check
	def checkBindingIdsAllDifferentInCompositeComponent(CompositeComponent c) {
		for(Binding b: c.bindings) {
			if (c.bindings.filter[it.name==b.name].size > 1) {
				error("Multiple bindings with the same id in a composite component",
					VecaDslPackage.Literals.COMPOSITE_COMPONENT__BINDINGS,
					MULTIPLE_BINDINGS_SAME_ID
				)
			}
		}
	}
	
	// [X] operations used in internal bindings do exist 
 	@Check
	def checkExistingOperationInBinding(BindingInformation b) {
		val p1 = b.point1
		val p2 = b.point2
		for(JoinPoint p : Arrays.asList(p1,p2)) {
			if (p instanceof IJoinPoint) {
				if (!p.checkJoinPoint()) {
					error('''Unknown operation «p.component.name».«p.operation»''', 
						getCause(p,p1,p2),
						UNKNOWN_OPERATION
					)
				}
			}
		}
	}
	
	// no self binding
	@Check
	def checkNoSelfBinding(Binding b) {
		if (b.binfo instanceof InternalBinding) {
			val p1 = b.binfo.point1 as IJoinPoint
			val p2 = b.binfo.point2 as IJoinPoint
			if (p1.component.name.equals(p2.component.name)) {
				error("Self-binding is not allowed",
					VecaDslPackage.Literals.BINDING__BINFO,
					SELF_BINDING
			)
			}
		}
	}

	// [X] internal bindings connect required to provided
	// [X] external bindings connect self.provided to provided or required to self.required
	@Check
	def checkCompatibleOperationsInComponentBindings(CompositeComponent c) {
		for(Binding b: c.bindings) {			
			if (b.binfo instanceof InternalBinding) {
				if (!checkCompatibleOperationsInComponentInternalBinding(c, b.binfo as InternalBinding)) {
					error(String.format("Binding %s is not correct (should be required --> provided)",b.name),
						VecaDslPackage.Literals.COMPOSITE_COMPONENT__BINDINGS,
						INCORRECT_BINDING
					)				
				}
			}	
			else if (b.binfo instanceof ExternalBinding) {
				if (!checkCompatibleOperationsInComponentExternalBinding(c, b.binfo as ExternalBinding)) {
					error(String.format("Binding %s is not correct (should be self.provided ==> provided or required ==> self.required)",b.name),
						VecaDslPackage.Literals.COMPOSITE_COMPONENT__BINDINGS,
						INCORRECT_BINDING
					)						
				}
			}
		}
	}
	
	// children in composites have different names
	@Check
	def checkChildrenHaveDifferentNames(CompositeComponent c) {
		for(NamedComponent sc: c.children)
			if(c.children.filter[it.name.equals(sc.name)].size>1) {
				error(String.format("There is more than one subcomponent named %s",sc.name),
					VecaDslPackage.Literals.COMPOSITE_COMPONENT__CHILDREN,
					INCORRECT_CHILDREN
				)
			}
	}
	
	// no multiple binding
	// for each binding b = c1.o1 -- c2.o2 in a composite
	// there should not exist a binding b' = c1'.o1' -- c2'.o2', different from b, in the composite
	// such that (c1=c1' and o1=o1') or (c1=c2' and o1=o2') or (c2=c2' and o2=o2') or (c2=c1' and o2=o1')
	@Check
	def noMultipleBindingOnOperations(CompositeComponent c) {
		for(Binding b : c.bindings) {
			if (c.bindings.exists[conflict(b.binfo,it.binfo)]) {
				error(String.format("Multiple binding at the same joint point (binding %s)", b.name),
					VecaDslPackage.Literals.COMPOSITE_COMPONENT__BINDINGS,
					NARY_BINDING
				)
			}
		}
	}
		
	// helpers
	
	// two bindings are in conflict if a join point of the first and a join point of the second are in conflict
	def conflict(BindingInformation bi1, BindingInformation bi2) {
		if (bi1==bi2)
			return false
		return (conflict(bi1.point1,bi2.point1) || conflict(bi1.point1,bi2.point2) || conflict(bi1.point2,bi2.point1) || conflict(bi1.point2,bi2.point2))
	}
	
	// two join point are in conflict if they represent the same binding point (same component, same id)
	def conflict(JoinPoint p1, JoinPoint p2) {
		if (p1 instanceof EJoinPoint && p2 instanceof EJoinPoint) {
			// both components are "self", we only have to check operations
			return (p1 as EJoinPoint).operation.name.equals((p2 as EJoinPoint).operation.name)
		}
		else if (p1 instanceof IJoinPoint && p2 instanceof IJoinPoint) {
			val sameop = (p1 as IJoinPoint).operation.equals((p2 as IJoinPoint).operation)
			val samecomp = (p1 as IJoinPoint).component==(p2 as IJoinPoint).component
			return sameop && samecomp	
		}
		else // cannot be in conflict, it is a join point in a composite and one in one of its children
			return false
	}

	def equals(Message m1, Message m2) {
		if (m1===null || m2===null)
			return m1===m2
		return m1.name.equals(m2.name)
	}
	
	def compatible(Operation o1, Operation o2) {
		return
			o1.name.equals(o2.name) &&
			equals(o1.inputMessage, o2.inputMessage) &&
			equals(o1.outputMessage, o2.outputMessage)
	}

	def findOperation(JoinPoint p) {	
		if (p instanceof EJoinPoint) {
			return p.operation
		}
		else {
			// can be null
			return (p as IJoinPoint).component.type.findOperation((p as IJoinPoint).operation)
		}	
	}
		
	def getCause(JoinPoint p, JoinPoint p1, JoinPoint p2) {
		if (p===p1)
			return VecaDslPackage.Literals.BINDING_INFORMATION__POINT1
		else
			return VecaDslPackage.Literals.BINDING_INFORMATION__POINT2
	}
	
	def checkJoinPoint(IJoinPoint p) {
		// p is x.o, we have to check there is an operation o in x
		return p.component.type.hasOperation(p.operation)
	}
	
	def hasOperation(Component c, String oname) {
		return isProvidedOperation(c,oname) || isRequiredOperation(c,oname)
	}
	
	def findOperation(Component c, String oname) {
		var o = c.signature.providedOps?.findFirst[it.name.equals(oname)]
		if (o === null) {
			o = c.signature.requiredOps?.findFirst[it.name.equals(oname)]
		}
		return o
	}
	
	def isProvidedOperation(Component c, String oname) {
		if(c.signature.providedOps !== null)
			return c.signature.providedOps.exists[it.name.equals(oname)]
		return false
	}
	
	def isRequiredOperation(Component c, String oname) {
		if(c.signature.requiredOps !== null)
			return c.signature.requiredOps.exists[it.name.equals(oname)]
		return false
	}
	
	// an internal binding c1.o1 --> c2.o2 is ok if o1 is required by c1 and o2 is provided by c2
	// note that for full correctness, we also have to use checkCompatibleOperationsInBinding (checks op names and messages)
	def checkCompatibleOperationsInComponentInternalBinding(CompositeComponent c, InternalBinding b) {
		val o1 = (b.point1 as IJoinPoint).operation
		val o2 = (b.point2 as IJoinPoint).operation
		val c1 = (b.point1 as IJoinPoint).component.type
		val c2 = (b.point2 as IJoinPoint).component.type
		return (isRequiredOperation(c1,o1) && isProvidedOperation(c2,o2))			
	}

	// an external binding c1.o1 ==> c2.o2 is ok if
	// - either o1 is provided by c1 and o2 is provided by c2 and c1 is self
	// - or o1 is required by c1 and o2 is required by c2 and c2 is self
	// note that for full correctness, we also have to use checkCompatibleOperationsInBinding (checks op names and messages)
	def checkCompatibleOperationsInComponentExternalBinding(CompositeComponent c, ExternalBinding b) {
		if (b.point1 instanceof EJoinPoint && b.point2 instanceof IJoinPoint) { // self.o1 ==> c2.o2
			val o1 = (b.point1 as EJoinPoint).operation.name
			val o2 = (b.point2 as IJoinPoint).operation
			val c1 = c
			val c2 = (b.point2 as IJoinPoint).component.type
			return (isProvidedOperation(c1,o1) && isProvidedOperation(c2,o2))
		}		
		else if (b.point1 instanceof IJoinPoint && b.point2 instanceof EJoinPoint) { // c1.o1 ==> self.o2
			val o1 = (b.point1 as IJoinPoint).operation
			val o2 = (b.point2 as EJoinPoint).operation.name
			val c1 = (b.point1 as IJoinPoint).component.type
			val c2 = c
			return (isRequiredOperation(c1,o1) && isRequiredOperation(c2,o2))	
		}
		else // not possible wrt the grammar
			return false
	}
	
}
<commit_msg>feat: check that events are ok wrt provided/required status of operations<commit_after>/*
 * generated by Xtext 2.13.0
 */
package fr.lip6.veca.ide.validation

import org.eclipse.xtext.validation.Check
import fr.lip6.veca.ide.vecaDsl.VecaDslPackage
import fr.lip6.veca.ide.vecaDsl.Component
import fr.lip6.veca.ide.vecaDsl.IJoinPoint
import fr.lip6.veca.ide.vecaDsl.JoinPoint
import java.util.Arrays
import fr.lip6.veca.ide.vecaDsl.Binding
import fr.lip6.veca.ide.vecaDsl.BindingInformation
import fr.lip6.veca.ide.vecaDsl.EJoinPoint
import fr.lip6.veca.ide.vecaDsl.Operation
import fr.lip6.veca.ide.vecaDsl.Message
import fr.lip6.veca.ide.vecaDsl.CompositeComponent
import fr.lip6.veca.ide.vecaDsl.InternalBinding
import fr.lip6.veca.ide.vecaDsl.ExternalBinding
import fr.lip6.veca.ide.vecaDsl.NamedComponent
import fr.lip6.veca.ide.vecaDsl.Model
import fr.lip6.veca.ide.vecaDsl.IJoinPoint
import fr.lip6.veca.ide.vecaDsl.CommunicationAction
import java.util.List
import fr.lip6.veca.ide.vecaDsl.CommunicationKind
import fr.lip6.veca.ide.vecaDsl.BasicComponent
import fr.lip6.veca.ide.vecaDsl.Transition
import fr.lip6.veca.ide.vecaDsl.Action
import fr.lip6.veca.ide.vecaDsl.TransitionAction

/**
 * This class contains custom validation rules. 
 *
 * See https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#validation
 */
class VecaDslValidator extends AbstractVecaDslValidator {
	
public static val INVALID_NAME = 'invalidName'
public static val UNKNOWN_OPERATION = "unknownOperation"
public static val INCOMPATIBLE_OPERATIONS = "incompatibleOperations"
public static val INCORRECT_BINDING = "incorrectBinding"
public static val INCORRECT_CHILDREN = "incorrectChildren"
public static val INCORRECT_TYPES = "incorrectTypes"
public static val SELF_COMPONENT = "selfComponent"
public static val SELF_BINDING = "selfBinding"
public static val MULTIPLE_BINDINGS_SAME_ID = "multipleBindingsWithSameId"
public static val NARY_BINDING = "naryBinding"
public static val INCORRECT_BEHAVIOR_EVENT = "incorrectBehaviorEvent"
public static val ERROR = "error"

	// all component types have different names
	@Check
	def checkComponentNamesAreDifferent(Model m) {
		for(Component c: m.types) {
			if(m.types.filter[it.name.equals(c.name)].size>1) {
				error(String.format("There is more than one component type named %s", c.name),
					VecaDslPackage.Literals.MODEL__TYPES,
					INCORRECT_TYPES
				)
			}
		}
	}

	// component type names begin with a capital
	@Check
	def checkComponentStartsWithCapital(Component c) {
		if (!Character.isUpperCase(c.name.charAt(0))) {
			warning('Component type name should start with a capital', 
					VecaDslPackage.Literals.COMPONENT__NAME,
					INVALID_NAME,
					c.name)
		}
	}
	
	// bindings relate compatible operations
	// two operations are compatible if:
	// [X] they have the same name
	// [X] they have the same message types
	// [X] for internal bindings connect required to provided (in Component using checkCompatibleOperationsInComponentBindings)
	// [X] for external bindings connect self.provided to provided or required to self.required (in Component using checkCompatibleOperationsInComponentBindings)
 	@Check
	def checkCompatibleOperationsInBinding(BindingInformation b) {
		val o1 = b.point1.findOperation()
		val o2 = b.point2.findOperation()
		if (o1 !== null && o2 !== null) {
			if (!compatible(o1,o2)) {
				error("Incompatible operations (names and/or messages)",
					VecaDslPackage.Literals.BINDING_INFORMATION__POINT1,
					INCOMPATIBLE_OPERATIONS
				)
			}
		}
	}
	
	// bindings in a composite component have different identifiers
	@Check
	def checkBindingIdsAllDifferentInCompositeComponent(CompositeComponent c) {
		for(Binding b: c.bindings) {
			if (c.bindings.filter[it.name==b.name].size > 1) {
				error("Multiple bindings with the same id in a composite component",
					VecaDslPackage.Literals.COMPOSITE_COMPONENT__BINDINGS,
					MULTIPLE_BINDINGS_SAME_ID
				)
			}
		}
	}
	
	// [X] operations used in internal bindings do exist 
 	@Check
	def checkExistingOperationInBinding(BindingInformation b) {
		val p1 = b.point1
		val p2 = b.point2
		for(JoinPoint p : Arrays.asList(p1,p2)) {
			if (p instanceof IJoinPoint) {
				if (!p.checkJoinPoint()) {
					error('''Unknown operation «p.component.name».«p.operation»''', 
						getCause(p,p1,p2),
						UNKNOWN_OPERATION
					)
				}
			}
		}
	}
	
	// no self binding
	@Check
	def checkNoSelfBinding(Binding b) {
		if (b.binfo instanceof InternalBinding) {
			val p1 = b.binfo.point1 as IJoinPoint
			val p2 = b.binfo.point2 as IJoinPoint
			if (p1.component.name.equals(p2.component.name)) {
				error("Self-binding is not allowed",
					VecaDslPackage.Literals.BINDING__BINFO,
					SELF_BINDING
			)
			}
		}
	}

	// [X] internal bindings connect required to provided
	// [X] external bindings connect self.provided to provided or required to self.required
	@Check
	def checkCompatibleOperationsInComponentBindings(CompositeComponent c) {
		for(Binding b: c.bindings) {			
			if (b.binfo instanceof InternalBinding) {
				if (!checkCompatibleOperationsInComponentInternalBinding(c, b.binfo as InternalBinding)) {
					error(String.format("Binding %s is not correct (should be required --> provided)",b.name),
						VecaDslPackage.Literals.COMPOSITE_COMPONENT__BINDINGS,
						INCORRECT_BINDING
					)				
				}
			}	
			else if (b.binfo instanceof ExternalBinding) {
				if (!checkCompatibleOperationsInComponentExternalBinding(c, b.binfo as ExternalBinding)) {
					error(String.format("Binding %s is not correct (should be self.provided ==> provided or required ==> self.required)",b.name),
						VecaDslPackage.Literals.COMPOSITE_COMPONENT__BINDINGS,
						INCORRECT_BINDING
					)						
				}
			}
		}
	}
	
	// children in composites have different names
	@Check
	def checkChildrenHaveDifferentNames(CompositeComponent c) {
		for(NamedComponent sc: c.children)
			if(c.children.filter[it.name.equals(sc.name)].size>1) {
				error(String.format("There is more than one subcomponent named %s",sc.name),
					VecaDslPackage.Literals.COMPOSITE_COMPONENT__CHILDREN,
					INCORRECT_CHILDREN
				)
			}
	}
	
	// no multiple binding
	// for each binding b = c1.o1 -- c2.o2 in a composite
	// there should not exist a binding b' = c1'.o1' -- c2'.o2', different from b, in the composite
	// such that (c1=c1' and o1=o1') or (c1=c2' and o1=o2') or (c2=c2' and o2=o2') or (c2=c1' and o2=o1')
	@Check
	def noMultipleBindingOnOperations(CompositeComponent c) {
		for(Binding b : c.bindings) {
			if (c.bindings.exists[conflict(b.binfo,it.binfo)]) {
				error(String.format("Multiple binding at the same joint point (binding %s)", b.name),
					VecaDslPackage.Literals.COMPOSITE_COMPONENT__BINDINGS,
					NARY_BINDING
				)
			}
		}
	}
	
	// events in behaviors are correct wrt operations
	@Check
	def checkEventsInBehaviors(BasicComponent c) {
		val providedOperations = c.signature.providedOps
		val requiredOperations = c.signature.requiredOps
		for(Transition t : c.behavior.transitions) {
			for(TransitionAction a : t.actions) {
				if (a.action instanceof CommunicationAction) {
					if (!isCorrectEvent(a.action as CommunicationAction, providedOperations, requiredOperations)) {
						error(eventErrorMessage(a.action as CommunicationAction).toString,
							VecaDslPackage.Literals.BASIC_COMPONENT__BEHAVIOR,
							INCORRECT_BEHAVIOR_EVENT
						)
					}
				}
			}
		}
	}
	
	// helpers
	
	def eventErrorMessage(CommunicationAction a) '''«IF (a.communicationKind == CommunicationKind.RECEIVE || a.communicationKind == CommunicationKind.REPLY)»
	receive/reply on operation «a.operation.name» is not possible
	«ELSEIF (a.communicationKind == CommunicationKind.INVOKE || a.communicationKind == CommunicationKind.RESULT)»
	invoke/result on operation «a.operation.name» is not possible
	«ELSE»
	unknown kind of event for «a»
	«ENDIF»
	'''
	
	// an event is correct wrt and operation o :
	// - if o is provided, only receive o, reply o, and tau are ok
	// - if o is required, only invoke o, result o, and tau are ok
	def isCorrectEvent(CommunicationAction a, List<Operation> providedOperations, List<Operation> requiredOperations) {
		if (a.communicationKind == CommunicationKind.RECEIVE || a.communicationKind == CommunicationKind.REPLY) {
			return providedOperations.contains(a.operation)
		} else if (a.communicationKind == CommunicationKind.INVOKE || a.communicationKind == CommunicationKind.RESULT) {
			return requiredOperations.contains(a.operation)			
		} else {
			return false;	
		}
	}
	
	// two bindings are in conflict if a join point of the first and a join point of the second are in conflict
	def conflict(BindingInformation bi1, BindingInformation bi2) {
		if (bi1==bi2)
			return false
		return (conflict(bi1.point1,bi2.point1) || conflict(bi1.point1,bi2.point2) || conflict(bi1.point2,bi2.point1) || conflict(bi1.point2,bi2.point2))
	}
	
	// two join point are in conflict if they represent the same binding point (same component, same id)
	def conflict(JoinPoint p1, JoinPoint p2) {
		if (p1 instanceof EJoinPoint && p2 instanceof EJoinPoint) {
			// both components are "self", we only have to check operations
			return (p1 as EJoinPoint).operation.name.equals((p2 as EJoinPoint).operation.name)
		}
		else if (p1 instanceof IJoinPoint && p2 instanceof IJoinPoint) {
			val sameop = (p1 as IJoinPoint).operation.equals((p2 as IJoinPoint).operation)
			val samecomp = (p1 as IJoinPoint).component==(p2 as IJoinPoint).component
			return sameop && samecomp	
		}
		else // cannot be in conflict, it is a join point in a composite and one in one of its children
			return false
	}

	def equals(Message m1, Message m2) {
		if (m1===null || m2===null)
			return m1===m2
		return m1.name.equals(m2.name)
	}
	
	def compatible(Operation o1, Operation o2) {
		return
			o1.name.equals(o2.name) &&
			equals(o1.inputMessage, o2.inputMessage) &&
			equals(o1.outputMessage, o2.outputMessage)
	}

	def findOperation(JoinPoint p) {	
		if (p instanceof EJoinPoint) {
			return p.operation
		}
		else {
			// can be null
			return (p as IJoinPoint).component.type.findOperation((p as IJoinPoint).operation)
		}	
	}
		
	def getCause(JoinPoint p, JoinPoint p1, JoinPoint p2) {
		if (p===p1)
			return VecaDslPackage.Literals.BINDING_INFORMATION__POINT1
		else
			return VecaDslPackage.Literals.BINDING_INFORMATION__POINT2
	}
	
	def checkJoinPoint(IJoinPoint p) {
		// p is x.o, we have to check there is an operation o in x
		return p.component.type.hasOperation(p.operation)
	}
	
	def hasOperation(Component c, String oname) {
		return isProvidedOperation(c,oname) || isRequiredOperation(c,oname)
	}
	
	def findOperation(Component c, String oname) {
		var o = c.signature.providedOps?.findFirst[it.name.equals(oname)]
		if (o === null) {
			o = c.signature.requiredOps?.findFirst[it.name.equals(oname)]
		}
		return o
	}
	
	def isProvidedOperation(Component c, String oname) {
		if(c.signature.providedOps !== null)
			return c.signature.providedOps.exists[it.name.equals(oname)]
		return false
	}
	
	def isRequiredOperation(Component c, String oname) {
		if(c.signature.requiredOps !== null)
			return c.signature.requiredOps.exists[it.name.equals(oname)]
		return false
	}
	
	// an internal binding c1.o1 --> c2.o2 is ok if o1 is required by c1 and o2 is provided by c2
	// note that for full correctness, we also have to use checkCompatibleOperationsInBinding (checks op names and messages)
	def checkCompatibleOperationsInComponentInternalBinding(CompositeComponent c, InternalBinding b) {
		val o1 = (b.point1 as IJoinPoint).operation
		val o2 = (b.point2 as IJoinPoint).operation
		val c1 = (b.point1 as IJoinPoint).component.type
		val c2 = (b.point2 as IJoinPoint).component.type
		return (isRequiredOperation(c1,o1) && isProvidedOperation(c2,o2))			
	}

	// an external binding c1.o1 ==> c2.o2 is ok if
	// - either o1 is provided by c1 and o2 is provided by c2 and c1 is self
	// - or o1 is required by c1 and o2 is required by c2 and c2 is self
	// note that for full correctness, we also have to use checkCompatibleOperationsInBinding (checks op names and messages)
	def checkCompatibleOperationsInComponentExternalBinding(CompositeComponent c, ExternalBinding b) {
		if (b.point1 instanceof EJoinPoint && b.point2 instanceof IJoinPoint) { // self.o1 ==> c2.o2
			val o1 = (b.point1 as EJoinPoint).operation.name
			val o2 = (b.point2 as IJoinPoint).operation
			val c1 = c
			val c2 = (b.point2 as IJoinPoint).component.type
			return (isProvidedOperation(c1,o1) && isProvidedOperation(c2,o2))
		}		
		else if (b.point1 instanceof IJoinPoint && b.point2 instanceof EJoinPoint) { // c1.o1 ==> self.o2
			val o1 = (b.point1 as IJoinPoint).operation
			val o2 = (b.point2 as EJoinPoint).operation.name
			val c1 = (b.point1 as IJoinPoint).component.type
			val c2 = c
			return (isRequiredOperation(c1,o1) && isRequiredOperation(c2,o2))	
		}
		else // not possible wrt the grammar
			return false
	}
	
}

{'fraction_non_alphanumeric': 0.1008729388942774, 'fraction_numerical': 0.016531016741871546, 'mean_word_length': 4.024152542372882, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 25, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, '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': '17936', 'n_tokens_mistral': 8919, 'n_tokens_neox': 7933, 'n_words': 2033}